NO.55十六届蓝桥杯备战|排序|插入|选择|冒泡|堆|快速|归并(C++)

devtools/2025/3/31 1:37:08/

插⼊排序

插⼊排序(Insertion Sort)类似于玩扑克牌插牌过程,每次将⼀个待排序的元素按照其关键字⼤⼩插⼊到前⾯已排好序的序列中,按照该种⽅式将所有元素全部插⼊完成即可

#include <iostream>  
using namespace std;  const int N = 1e5 + 10;  
int n;  
int a[N];  void insert_sort()  
{  // 依次枚举待排序的元素  for(int i = 2; i <= n; i++) // 第⼀个位置默认就是有序的  {  int key = a[i];  // 前⾯⽐ key ⼤的,统⼀右移  int j = i - 1;  while(j >= 1 && a[j] > key)  {  a[j + 1] = a[j];  j--;  }  a[j + 1] = key;  }  
}int main()  
{  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  insert_sort();  for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

时间复杂度

  • 当整个序列有序的时候,插⼊排序最优,此时时间复杂度为O(n) 。
  • 当整个序列逆序的时候,每个元素都要跑到最前⾯,时间复杂度为O(n2)

选择排序

选择排序(Selection Sort)是⼀种特别直观的排序算法。每次找出未排序序列中最⼩的元素,然后放进有序序列的后⾯

#include <iostream>  
using namespace std;  
const int N = 1e5 + 10;  
int n;  
int a[N];  
void selection_sort()  
{  for(int i = 1; i < n; i++) // 待排序区间的⾸位置  {  // [i, n] 区间就是待排序的区间  int pos = i;for(int j = i + 1; j <= n; j++) // 查找待排序区间最⼩的元素的下标  {  if(a[j] < a[pos])  {  pos = j;  }  }  swap(a[i], a[pos]);  }  
}  
int main()  
{  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  selection_sort();  for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

【时间复杂度】

  • ⽆论数组有序还是⽆序,在未排序区间内找最⼩值的时候,都需要遍历⼀遍待排序的区间,因此时间复杂度为O(n2)

冒泡排序

冒泡排序(Bubble Sort)也是⼀种简单的排序算法。它的⼯作原理是每次检查相邻两个元素,如果前⾯的元素与后⾯的元素满⾜给定的排序条件,就将相邻两个元素交换。当没有相邻的元素需要交换时,排序就完成了。
由于在算法的执⾏过程中,较⼤的元素像是⽓泡般慢慢浮到数列的末端,故叫做冒泡排序

#include <iostream>
using namespace std;  
const int N = 1e5 + 10;  
int n;  
int a[N];  
// 优化后的冒泡排序  
void bubble_sort()  
{  // 依次枚举待排序区间的最后⼀个元素  for(int i = n; i > 1; i--)  {  bool flag = false;  // [1, i] 就是待排序区间  for(int j = 1; j < i; j++)  {  if(a[j] > a[j + 1])  {  swap(a[j], a[j + 1]);  flag = true;  }  }  if(flag == false) return;  }  
}  
int main()  
{  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  bubble_sort();  for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

时间复杂度

  • 如果数组有序,只会扫描⼀遍,时间复杂度为O(n) 。
  • 如果逆序,所有元素都会向后冒⼀次到合适位置,时间复杂度为O(n2)

堆排序

堆排序(Heap Sort)是指利⽤堆这种数据结构所设计的⼀种排序算法。本质上是优化了选择排序算法,如果将数据放在堆中,能够快速找到待排序元素中的最⼩值或最⼤值。
堆排序的过程分两步:

  1. 建堆。升序建⼤堆,降序建⼩堆。
    建堆过程,从倒数第⼀个⾮叶⼦节点开始,倒着⼀直到根结点位置,每个结点进⾏向下调整。
  2. 排序。删除堆顶的逻辑。
    每次将堆顶元素与堆中最后⼀个元素交换,然后将堆顶元素往下调整。直到堆中剩余⼀个元素,所有元素就都有序了。
    因此,堆排序仅需⽤到向下调整算法
#include <iostream>  
using namespace std;  
const int N = 1e5 + 10;  
int n;  
int a[N];  
void down(int parent, int len)  
{  int child = parent * 2;  while(child <= len)  {  if(child + 1 <= len && a[child + 1] > a[child]) child++;  if(a[parent] >= a[child]) return;swap(a[parent], a[child]);  parent = child;  child = parent * 2;  }  
}  
void heap_sort()  
{  // 1. 建堆  for(int i = n / 2; i >= 1; i--)  {  down(i, n);  }  // 2. 排序  for(int i = n; i > 1; i--) // 枚举堆⾥⾯最后⼀个元素的位置  {  swap(a[1], a[i]);  down(1, i - 1);  }  
}  
int main()  
{  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  heap_sort();  for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

时间复杂度
时间复杂度需要计算两部分:⼀部分是建堆的时间复杂度,另⼀部分是排序。

  • 排序部分的时间复杂度很好估计,每次都是从堆中选择⼀个最⼤值,然后与最后⼀个元素交换,然后调整。⼀次选择的时间复杂度为log n ,⼀共执⾏n 次,⼤概就是n log n 级别。
  • 建堆的时间复杂度:
    计算向下调整算法建堆时间复杂度
    ![[Pasted image 20250322161630.png]]

则需要移动结点总的移动步数为:每层结点个数*向下调整次数
T ( h ) = 2 h − 1 − h T(h) = 2^h-1-h T(h)=2h1h
根据⼆叉树的性质:n = 2^h - 1 和h = log2 (n + 1)
T ( n ) = n − log ⁡ 2 ( n + 1 ) ≈ n T(n) = n - \log_{2}(n+1) \approx n T(n)=nlog2(n+1)n

综上所述,堆排序的时间复杂度主要取决于排序的过程,也就是n log n

快速排序

快速排序(Quick Sort),既然敢起这样的名字,说明它是常⻅排序算法中较为优秀的。事实上,在很多情况下,快排确实是效率较⾼的算法

算法原理

常规快排:在待排序区间中任取⼀个元素作为枢轴(pivot,或称基准值,通常选取区间⾸元素),然后按照基准值⼤⼩将区间中元素分割成左右两部分,左侧区间中元素⼩于基准值,右侧区间中元素⼤于等于基准值,即基准值已经放在其该放的位置上,该过程称为⼀次划分。划分结束后,再递归排基准值左侧,递归排基准值右侧即可
![[Pasted image 20250322163740.png]]

优化⼀:三路划分

三路划分优化:其实可以做到按照基准元素,将所有元素分成三个区间。左部分全部⼩于pivot,中间部分全部等于pivot,右部分全部⼤于pivot。然后中间部分就不⽤管了,直接递归处理左右部分

  1. 从区间中任取一个元素作为基准值,一般取区间最左侧元素,然后按照基准值对区间中元素进行划分
    ![[Pasted image 20250322164110.png]]

  2. 递归排基准值左侧;递归排基准值右侧
    ⽽这个三路划分,就是典型的数组分三块算法

优化⼆:随机选择基准元素

选择基准元素的⽅式:

  • 每次选择待排序序列的最左边元素
    那么,当整个序列有序的时候,每次递归就会划分出特别⻓的⼀段右区间,递归的层数是n次,每次要遍历整个数组⼀遍,时间复杂度就退化成n^2 。
    每次选择最右边元素也是同理。
  • 每次选择待排序序列的中间元素
    可以构造⼀个特殊的序列,使得每次取中间元素的时候都会取到最⼩值,依旧会退化成n^2。
  • 随机选择基准元素
    每次选择基准元素的时候,都从待排序的序列中随机选择⼀个数。在随机性的前提下,可以证明算法的时间复杂度趋近于nlogn 。
    因此,每次选择基准元素时,都使⽤随机函数选择
C++中的随机函数

C++提供了函数srand和rand,搭配使⽤可以返回⼀个随机值

#include <iostream>  
#include <ctime>  
using namespace std;  
int main()  
{  srand(time(0)); // 种下⼀个随机数种⼦  for(int i = 1; i <= 10; i++)  {  cout << rand() << endl; // 每次⽣成⼀个随机值  }  return 0;  
}

![[Pasted image 20250322171920.png]]

left和right指向第一个和最后一个元素,假设p等于4
![[Pasted image 20250322172121.png]]

i从第一个元素往最后一个元素遍历
现在ai是4,和p一样大,i++
![[Pasted image 20250322180638.png]]

ai是2,比p小,交换到前面,++l和i交换,i++
![[Pasted image 20250322180758.png]]

现在ai是4,和p一样大,i++
![[Pasted image 20250322180822.png]]

现在ai是5,比p大,交换到最右边,i和–r交换
![[Pasted image 20250322180911.png]]

i这是下标为4,ai为1,比p小,++l和i交换,i++
![[Pasted image 20250322181036.png]]

这是i等于5,r也是5,遍历结束,此时p=4,两个4已经在正确的位置上
中间区域也就是相等的区域,是l+1到r-1
左边比p小的区域是left到l,右边比p大的区域是r到right
接着遍历left到l和r到right

#include <iostream>  
#include <ctime>  
using namespace std;  
const int N = 1e5 + 10;  
int n;  
int a[N];  
// 优化⼀:随机选择基准元素  
int get_random(int left, int right)  
{  return a[rand() % (right - left + 1) + left];  
}  void quick_sort(int left, int right)  
{  if(left >= right) return;  // 1. 选择⼀个基准元素  int p = get_random(left, right);  // 2. 数组分三块 - 荷兰国旗问题  int l = left - 1, i = left, r = right + 1;  while(i < r)  {  if(a[i] < p) swap(a[++l], a[i++]);  else if(a[i] == p) i++;  else swap(a[--r], a[i]);  }  // [left, l] [l + 1, r - 1] [r, right]  quick_sort(left, l);  quick_sort(r, right);  
}  
int main()  
{  srand(time(0));  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  quick_sort(1, n);for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

时间复杂度

  • 如果每次基准元素都选择得当,数组划分的比较均匀,时间复杂度=递归层数*N*logN
  • 如果划分不当,数组分布比较极端,时间复杂度退化成N^2

归并排序

归并排序(Merge Sort)是⽆论数据有什么特性,时间复杂度就能稳定O(n log n) 的排序算法
归并排序⽤的是分治思想,不知道分治是啥也没关系,后续算法课会讲到。它的主要过程分两步:

  1. 将整个区间从中间⼀分为⼆,先把左边和右边排排序;
  2. 然后将左右两个有序的区间合并在⼀起。
    其中,如何让左右两边有序,就继续交给归并排序,因此归并排序是⽤递归来实现的;合并两个有序区间的操作,在顺序表中讲过类似的算法
#include <iostream>  
using namespace std;  
const int N = 1e5 + 10;  
int n;  
int a[N];  
int tmp[N]; // 辅助归并排序时,合并两个有序数组  
void merge_sort(int left, int right)  
{  if(left >= right) return;  // 1. 先⼀分为⼆  int mid = (left + right) >> 1;  // [left, mid] [mid + 1, right]  // 2. 先让左右区间有序  merge_sort(left, mid);  merge_sort(mid + 1, right);// 3. 合并两个有序数组  int cur1 = left, cur2 = mid + 1, i = left;  // [left, mid] [mid + 1, right]  while(cur1 <= mid && cur2 <= right)  {  if(a[cur1] <= a[cur2]) tmp[i++] = a[cur1++];  else tmp[i++] = a[cur2++];  }  while(cur1 <= mid) tmp[i++] = a[cur1++];  while(cur2 <= right) tmp[i++] = a[cur2++];  for(int j = left; j <= right; j++)  {  a[j] = tmp[j];  }  
}  int main()  
{  cin >> n;  for(int i = 1; i <= n; i++) cin >> a[i];  merge_sort(1, n);  for(int i = 1; i <= n; i++) cout << a[i] << " ";  return 0;  
}

【时间复杂度】
每次递归都是标准的⼀分为⼆,因此时间复杂度为O(n log n)


http://www.ppmy.cn/devtools/171840.html

相关文章

行业大数据实验报告 通过聚类算法实现睡眠健康群体的精准智能划分

一、实验目标 本实验旨在通过聚类算法对睡眠健康群体进行多维特征分析&#xff0c;识别不同群体的睡眠模式&#xff0c;并根据群体特点制定个性化的睡眠改善方案。通过使用聚类算法&#xff0c;帮助理解不同群体的睡眠健康状况&#xff0c;为个性化健康管理提供支持。 二、实验…

华为OD机试2025A卷 - 游戏分组/王者荣耀(Java Python JS C++ C )

最新华为OD机试 真题目录:点击查看目录 华为OD面试真题精选:点击立即查看 题目描述 2020年题: 英雄联盟是一款十分火热的对战类游戏。每一场对战有10位玩家参与,分为两组,每组5人。每位玩家都有一个战斗力,代表着这位玩家的厉害程度。为了对战尽可能精彩,我们需要…

黑天鹅事件频发:2025年5种蒙特卡洛模拟工具压力测试

——当不确定性成为常态 2025年&#xff0c;全球经济在供应链动荡、技术迭代加速和气候异常的叠加冲击下&#xff0c;黑天鹅事件从“偶然”演变为“新常态”。企业如何在高风险环境中保持决策韧性&#xff1f;答案藏在蒙特卡洛模拟工具与 PLM&#xff08;产品生命周期管理&…

【Linux探索学习】第二十九弹——线程概念:Linux线程的基本概念与线程控制详解

Linux学习笔记&#xff1a; https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言&#xff1a; 在现代操作系统中&#xff0c;线程是程序执行流的最小单元。与进程相比&#xff0c;线程更加轻量级&#xff0c;创建和销毁的开销更小&…

体育赛事即时比分 分析页面的开发技术架构与实现细节

本文基于“体育即时比分系统”的实际开发经验总结&#xff0c;仅供技术交流。该系统在实现过程中&#xff0c;主要解决了实时比分更新、赔率数据同步、赛事分析展示等关键问题&#xff0c;并采用了以下技术栈&#xff1a; 后端&#xff1a;PHP&#xff08;ThinkPHP 框架&#…

【Linux】进程间通信——共享内存

文章目录 共享内存&#xff08;Shared Memory&#xff09;什么是共享内存2. 共享内存的特点3.共享内存的主要函数3.1.shmget()3.2.shmat3.3.shmdt3.4.shmctl 共享内存实现进程间通信ShareMemory.hppServer.ccClient.cc 总结 共享内存&#xff08;Shared Memory&#xff09; 什…

智能汽车图像及视频处理方案,支持视频实时拍摄特效能力

在智能汽车日新月异的今天&#xff0c;美摄科技作为智能汽车图像及视频处理领域的先行者&#xff0c;凭借其卓越的技术实力和前瞻性的设计理念&#xff0c;为全球智能汽车制造商带来了一场视觉盛宴的革新。美摄科技推出智能汽车图像及视频处理方案&#xff0c;一个集高效性、智…

游戏引擎学习第177天

仓库:https://gitee.com/mrxiao_com/2d_game_4 今日计划 调试代码有时可能会非常困难&#xff0c;尤其是在面对那些难以发现的 bug 时。显然&#xff0c;调试工具是其中一个非常重要的工具&#xff0c;但在游戏开发中&#xff0c;另一个非常常见的工具就是自定义的调试工具&a…