- 时间复杂度
- 空间复杂度
- 排序算法稳定性
排序算法稳定性的定义:
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
以下按照自己的想法总结的内容:
想看详细可以观看下面的博客,有详细的流程图:
十大排序算法-快排-希尔-堆排-归并-冒泡-桶排-选择-插入-计数-基数-1_天津 唐秙的博客-CSDN博客
| 排序算法 | 平均时间复杂度 | 最坏的时间复杂度 | 最好的时间复杂度 | 空间复杂度 | 稳定性 |
| 冒泡排序 | O(n^2) | O(n^2) | O(n) | O(1) | 稳定 |
| 选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 数组不稳定、链表稳定 |
| 插入排序 | O(n^2) | O(n^2) | O(n) | O(1) | 稳定 |
| 快速排序 | O(n*log2(n)) | O(n^2) | O(n^2) | O(log2n) | 不稳定 |
| 计数排序 | O(n+m) | O(n+m) | O(n+m) | O(n+m) | 稳定 |
| 堆排序 | O(n*log2(n)) | O(n*log2(n)) | O(nlog2(n)) | O(1) | 不稳定 |
| 希尔排序 | O(n*log2(n)) | O(n^2) | O(nlog2(n)) | O(1) | 不稳定 |
| 归并排序 | O(n*log2(n)) | O(n*log2(n)) | O(nlog2(n)) | O(n) | 稳定 |
| 桶排序 | O(n+k) | O(n^2) | O(n^2) | O(n+k) | 稳定 |
| 基数排序 | O(k*n) | O(k*n) | O(k*n) | O(k+n) | 稳定 |
实现思路:从第一个数据开始,拿该数据和其他数据轮番比较,(从小到大排序)如果该数据大于其他数据的话,则交换数据
- //升序
- //从前往后排
- void Bubbling_sort(vector<int>& p)//冒泡排序
- {
- for (int i = 0; i < p.size()-1; i++)//从第一个数据开始遍历,最后一个不需要遍历所以-1
- {
- for (int j = i+1; j < p.size(); j++)//遍历的次数为数组长度减去已找到的数据
- {
- if (p[i] > p[j ])
- {
- swap(p[i], p[j]);
- }
- }
- }
- }
- //从后往前排
- void Bubbling_sort(vector<int>& p)//冒泡排序
- {
- for (int i = 0; i < p.size()-1; i++)//从第一个数据开始遍历,最后一个不需要遍历所以-1
- {
- for (int j = 0; j < p.size()- 1-i; j++)//遍历的次数为数组长度减去已找到的数据
- {
- if (p[j] > p[j + 1])
- {
- swap(p[j], p[j + 1]);
- }
- }
- }
- }
时间复杂度:O(n^2) 空间复杂度:O(1) 排序算法稳定
实现思路:从第一个数据开始,用一个数据来存储下标,找到最小数的下标,然后交换这两个数。
- //升序
- void Select_Sort(vector<int>& p)//选择排序
- {
- int min;//存储下标
- for (int i = 0; i < p.size()-1; i++)//只需要遍历i-1次
- {
- min = i;//假设第一个数为最小
- for (int j = i + 1; j < p.size(); j++)
- {
- if (p[j] < p[min])//找到小值
- {
- min = j;//更换下标
- }
- }
- if (min != i)//如果下标不匹配
- {
- swap(p[i],p[min]);//交换数值
- }
- }
- }
时间复杂度:O(n^2) 空间复杂度:O(1) 排序算法不稳定(链表的话稳定)
不稳定的体现:

实现思路:假设第一个已排序,从第二个数据开始,与前面的数据进行比较,如果比前面的数据小,则交换两个数据,如果比前面的数据大,则结束。

- //升序
- void Insert_Sort(vector<int>& p)//插入排序
- {
- for (int i = 1; i < p.size(); i++)//从第二个数据开始
- {
-
- if (p[i] < p[i - 1])//当前面的数大于该数时
- {
- int data = p[i];//获取插入的数据
- int n = i-1;//获取i-1的位置
- p[n+1] = p[n];
- while (n > 0 && data < p[n-1])
- {
- p[n] = p[n - 1];//把数据前移
- n--;//位置往后移
- }
- p[n] = data;//找到位置插入
- }
- }
- }
时间复杂度:O(n^2) 空间复杂度:O(1) 排序算法稳定
实现思路:(分治算法基础上设计出来的一种排序算法)
- 先取一个值P作为中轴,
- 把比它小的数放到左边
- 把比它大的数放到右边
- 重复对左右两个区域进行1-3步。
- void Quick_Sort(vector<int>& p, int begin, int end)//快速排序
- {
- if (begin >= end)//结束条件
- return;
- int head = begin;//头指针
- int tail = end;//尾指针
- int data = p[begin];//以头节点为中轴
- while (head < tail)//当头小于尾时执行
- {
- //从右边开始找小的数
- while (head<tail && p[tail]>=data) { tail--; }//如果尾部的数大于中枢的值,跳过,指向上一个
- p[head] = p[tail];//把值给赋给头指针
- //从左边开始找大的数
- while (head < tail && p[head]<= data) { head++; }//头部的值小于中枢的值,跳过,指向下一个
- p[tail] = p[head];//把值给赋给头指针
- }
- //当两个指针相等时就是中枢节点的位置
- p[head] = data;
- Quick_Sort(p, begin, head - 1);//递归左半部分
- Quick_Sort(p, head + 1,end );//递归右半部分
- }
时间复杂度:O(nlog2n) 空间复杂度:O(log2n)) 排序算法不稳定
实现思路为:
- 获取数组中的最大最小值,获取两个数的差值
- 开辟一个数组,大小为 :差值+1 初始化为0
- 利用新数组来存储,元素的个数
- 得到个数后,从头开始输出数组元素并赋值给原数组
- void Count_Sort(vector<int>& p)//计数排序
- {
- int min = 0;//存储最小值
- int max = 0;//存储最大值
- for (int i = 0; i < p.size(); i++)
- {
- if (p[i] < min)min = p[i];//找最小值
- if (p[i] > min)max = p[i];//找最大值
- }
- int num = max - min;//存储差值,用来创建数组
- vector<int>count(num + 1, 0);//用来存储元素个数
- for (int i = 0; i < p.size(); i++)
- {
- count[p[i - min]]++;//统计元素个数
- }
- int j = 0;
- for (int i = 0; i < p.size();)
- {
- while (j <= num && count[j])
- {
- count[j--];//个数减一
- p[i] = j + min;//把数值放到数值中
- i++;
- }
- j++;
- }
- }
时间复杂度:O(n+m) 空间复杂度:O(n+m) 排序算法稳定
推荐一个视频方便理解:
实现思路:
- 建堆 小到大排序(建大堆),大到小排序(建小堆)
- 替换第一个和最后一个数据,把树的元素个数减1
- 重复1-2,直到只剩一个元素
建堆的一些概念:

- void heapify(vector<int>& p, int n, int i)//建堆,n为数组长度,i为待维护节点下标
- {
- int mid = i;//
- int lchild = i * 2 + 1;//i的左节点
- int rchild = i * 2 + 2;//i的右节点
- //获取三点的最大值
- if (lchild < n && p[mid] < p[lchild])
- {
- mid = lchild;
- }
- if (lchild < n && p[mid] < p[rchild])
- {
- mid = rchild;
- }
- //如果最大值不为父节点
- if (mid != i)
- {
- swap(p[mid], p[i]);//交换数值,使父节点成为最大值
- heapify(p, n, mid);//继续比较完成建堆
- }
- }
- void Heap_Sort(vector<int>& p, int n)
- {
- //建堆
- int i;
- //由于n的父节点为(n-1)/2 相当于(n-1-1)/2=n/2-1
- for (i = n / 2 - 1; i >= 0; i--)//从后往前建堆
- {
- heapify(p, n, i);
- }
- //排序
- for (i = n - 1; i >= 0; i++)
- {
- swap(p[0], p[i]);//交换第一和最后一个值
- heapify(p, i, 0);//调整位置
- }
- }
时间复杂度:O(n*log2n) 空间复杂度:O(1) 排序算法不稳定
实现思路:
是插入排序的增强版,希尔排序将数组分隔成n组来进行插入排序,每一次,将数据排的相对有序。
- void shell_Sort(vector<int>& p)//希尔排序
- {
- int n = p.size();//获取数组长度
- //设置增量,每次循环/2
- for (int i = n / 2; i > 0; i /= 2)
- {
- //起始值从i开始,进行插入排序
- for (int j=i; j < n; j++)
- {
- int k = i;
- int temp = p[k];
- while (k - i >= 0 && p[k - i] > temp)
- {
- p[k] = p[k - i];
- k = k - i;
- }
- p[k] = temp;
- }
- }
- }
时间复杂度:O(n*log2n) 空间复杂度:O(1) 排序算法不稳定
方法为:
- 先把长度为n的数据分为两块
- 通过递归对左右半区进行拆分
- 拆分完后,分别对左右两边的数据,进行对比(排序),放入临时数组中
- 然后拷贝到数组中
排序算法:归并排序【图解+代码】_哔哩哔哩_bilibili
- void merge(vector<int>& n, vector<int>& m, int left , int mid, int right)合并
- {
- //左边第一个元素
- int x = left;
- //右边第一个元素
- int y = mid + 1;
- //记录临时数组下标
- int z = left;
- while (x <= mid && y <= right)
- {
- if (n[x] < n[y])
- {
- m[z++] = n[x++];
- }
- else
- {
- m[z++] = n[y++];
- }
- }
- //左边还有剩余元素
- while (x <= mid)
- {
- m[z++] = n[x++];
- }
- //右边还有剩余元素
- while (y <= right)
- {
- m[z++] = n[y++];
- }
- //把数据复制给数组
- while (left <= right)
- {
- n[left] = m[left];
- left++;
- }
- }
- void msort(vector<int>& n, vector<int>& m, int left, int right)
- {
- if (left < right)
- {
- int mid = left + (right - left) / 2;//取中间点
- //递归拆分左边
- msort(n, m, left, mid);
- //递归拆分右边
- msort(n, m, mid + 1, right);
- //合并
- merge(n,m,left,mid,right);
- }
- }
- void merge_Sort(vector<int>&p,int n)//归并排序
- {
- vector<int>G(n, 0);//辅助数组
- msort(p, G, 0, n - 1);//归并入口
- }
类似于哈希表,解决冲突的方法为地址法,把每个头节点当作一个桶。
把数据分别放入对应的桶中,然后从第一个桶开始,取数据放到数组中
基数排序:
- 取得数组中的最大数,并取得位数。
- arr为原始数组,从最低位开始取每个位组成radix数组。
- 对radix进行计数排序(利用计数排序适用于小范围数的特点)