• 常见的排序算法


    排序算法比较的几个点:

    • 时间复杂度
    • 空间复杂度
    • 排序算法稳定性

    排序算法稳定性的定义:

    假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,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)稳定

    冒泡排序:

    实现思路:从第一个数据开始,拿该数据和其他数据轮番比较,(从小到大排序)如果该数据大于其他数据的话,则交换数据

    1. //升序
    2. //从前往后排
    3. void Bubbling_sort(vector<int>& p)//冒泡排序
    4. {
    5. for (int i = 0; i < p.size()-1; i++)//从第一个数据开始遍历,最后一个不需要遍历所以-1
    6. {
    7. for (int j = i+1; j < p.size(); j++)//遍历的次数为数组长度减去已找到的数据
    8. {
    9. if (p[i] > p[j ])
    10. {
    11. swap(p[i], p[j]);
    12. }
    13. }
    14. }
    15. }
    16. //从后往前排
    17. void Bubbling_sort(vector<int>& p)//冒泡排序
    18. {
    19. for (int i = 0; i < p.size()-1; i++)//从第一个数据开始遍历,最后一个不需要遍历所以-1
    20. {
    21. for (int j = 0; j < p.size()- 1-i; j++)//遍历的次数为数组长度减去已找到的数据
    22. {
    23. if (p[j] > p[j + 1])
    24. {
    25. swap(p[j], p[j + 1]);
    26. }
    27. }
    28. }
    29. }

    时间复杂度:O(n^2)      空间复杂度:O(1)       排序算法稳定

    选择排序: 

    实现思路:从第一个数据开始,用一个数据来存储下标,找到最小数的下标,然后交换这两个数。

    1. //升序
    2. void Select_Sort(vector<int>& p)//选择排序
    3. {
    4. int min;//存储下标
    5. for (int i = 0; i < p.size()-1; i++)//只需要遍历i-1
    6. {
    7. min = i;//假设第一个数为最小
    8. for (int j = i + 1; j < p.size(); j++)
    9. {
    10. if (p[j] < p[min])//找到小值
    11. {
    12. min = j;//更换下标
    13. }
    14. }
    15. if (min != i)//如果下标不匹配
    16. {
    17. swap(p[i],p[min]);//交换数值
    18. }
    19. }
    20. }

    时间复杂度:O(n^2)      空间复杂度:O(1)        排序算法不稳定(链表的话稳定)

    不稳定的体现:

    插入排序: 

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

    1. //升序
    2. void Insert_Sort(vector<int>& p)//插入排序
    3. {
    4. for (int i = 1; i < p.size(); i++)//从第二个数据开始
    5. {
    6. if (p[i] < p[i - 1])//当前面的数大于该数时
    7. {
    8. int data = p[i];//获取插入的数据
    9. int n = i-1;//获取i-1的位置
    10. p[n+1] = p[n];
    11. while (n > 0 && data < p[n-1])
    12. {
    13. p[n] = p[n - 1];//把数据前移
    14. n--;//位置往后移
    15. }
    16. p[n] = data;//找到位置插入
    17. }
    18. }
    19. }

     时间复杂度:O(n^2)      空间复杂度:O(1)        排序算法稳定

    快速排序:

     实现思路:(分治算法基础上设计出来的一种排序算法

    1. 先取一个值P作为中轴,
    2. 把比它小的数放到左边
    3. 把比它大的数放到右边
    • 重复对左右两个区域进行1-3步。
    1. void Quick_Sort(vector<int>& p, int begin, int end)//快速排序
    2. {
    3. if (begin >= end)//结束条件
    4. return;
    5. int head = begin;//头指针
    6. int tail = end;//尾指针
    7. int data = p[begin];//以头节点为中轴
    8. while (head < tail)//当头小于尾时执行
    9. {
    10. //从右边开始找小的数
    11. while (head<tail && p[tail]>=data) { tail--; }//如果尾部的数大于中枢的值,跳过,指向上一个
    12. p[head] = p[tail];//把值给赋给头指针
    13. //从左边开始找大的数
    14. while (head < tail && p[head]<= data) { head++; }//头部的值小于中枢的值,跳过,指向下一个
    15. p[tail] = p[head];//把值给赋给头指针
    16. }
    17. //当两个指针相等时就是中枢节点的位置
    18. p[head] = data;
    19. Quick_Sort(p, begin, head - 1);//递归左半部分
    20. Quick_Sort(p, head + 1,end );//递归右半部分
    21. }

     时间复杂度:O(nlog2n)      空间复杂度:O(log2n))        排序算法不稳定 

    计数排序:

    实现思路为:

    • 获取数组中的最大最小值,获取两个数的差值
    • 开辟一个数组,大小为 :差值+1   初始化为0
    • 利用新数组来存储,元素的个数
    • 得到个数后,从头开始输出数组元素并赋值给原数组
    1. void Count_Sort(vector<int>& p)//计数排序
    2. {
    3. int min = 0;//存储最小值
    4. int max = 0;//存储最大值
    5. for (int i = 0; i < p.size(); i++)
    6. {
    7. if (p[i] < min)min = p[i];//找最小值
    8. if (p[i] > min)max = p[i];//找最大值
    9. }
    10. int num = max - min;//存储差值,用来创建数组
    11. vector<int>count(num + 1, 0);//用来存储元素个数
    12. for (int i = 0; i < p.size(); i++)
    13. {
    14. count[p[i - min]]++;//统计元素个数
    15. }
    16. int j = 0;
    17. for (int i = 0; i < p.size();)
    18. {
    19. while (j <= num && count[j])
    20. {
    21. count[j--];//个数减一
    22. p[i] = j + min;//把数值放到数值中
    23. i++;
    24. }
    25. j++;
    26. }
    27. }

     时间复杂度:O(n+m)      空间复杂度:O(n+m)        排序算法稳定 

    堆排序: 

    推荐一个视频方便理解:

    排序算法:堆排序【图解+代码】_哔哩哔哩_bilibili

    实现思路:

    1. 建堆 小到大排序(建大堆),大到小排序(建小堆)
    2. 替换第一个和最后一个数据,把树的元素个数减1
    3. 重复1-2,直到只剩一个元素

    建堆的一些概念:

    1. void heapify(vector<int>& p, int n, int i)//建堆,n为数组长度,i为待维护节点下标
    2. {
    3. int mid = i;//
    4. int lchild = i * 2 + 1;//i的左节点
    5. int rchild = i * 2 + 2;//i的右节点
    6. //获取三点的最大值
    7. if (lchild < n && p[mid] < p[lchild])
    8. {
    9. mid = lchild;
    10. }
    11. if (lchild < n && p[mid] < p[rchild])
    12. {
    13. mid = rchild;
    14. }
    15. //如果最大值不为父节点
    16. if (mid != i)
    17. {
    18. swap(p[mid], p[i]);//交换数值,使父节点成为最大值
    19. heapify(p, n, mid);//继续比较完成建堆
    20. }
    21. }
    22. void Heap_Sort(vector<int>& p, int n)
    23. {
    24. //建堆
    25. int i;
    26. //由于n的父节点为(n-1/2 相当于(n-1-1/2=n/2-1
    27. for (i = n / 2 - 1; i >= 0; i--)//从后往前建堆
    28. {
    29. heapify(p, n, i);
    30. }
    31. //排序
    32. for (i = n - 1; i >= 0; i++)
    33. {
    34. swap(p[0], p[i]);//交换第一和最后一个值
    35. heapify(p, i, 0);//调整位置
    36. }
    37. }

     时间复杂度:O(n*log2n)      空间复杂度:O(1)        排序算法不稳定 

    希尔排序: 

    实现思路:

    是插入排序的增强版,希尔排序将数组分隔成n组来进行插入排序,每一次,将数据排的相对有序。

    1. void shell_Sort(vector<int>& p)//希尔排序
    2. {
    3. int n = p.size();//获取数组长度
    4. //设置增量,每次循环/2
    5. for (int i = n / 2; i > 0; i /= 2)
    6. {
    7. //起始值从i开始,进行插入排序
    8. for (int j=i; j < n; j++)
    9. {
    10. int k = i;
    11. int temp = p[k];
    12. while (k - i >= 0 && p[k - i] > temp)
    13. {
    14. p[k] = p[k - i];
    15. k = k - i;
    16. }
    17. p[k] = temp;
    18. }
    19. }
    20. }

    时间复杂度:O(n*log2n)      空间复杂度:O(1)        排序算法不稳定 

    归并算法:

    方法为:

    • 先把长度为n的数据分为两块
    • 通过递归对左右半区进行拆分
    • 拆分完后,分别对左右两边的数据,进行对比(排序),放入临时数组中
    • 然后拷贝到数组中

    排序算法:归并排序【图解+代码】_哔哩哔哩_bilibili

    1. void merge(vector<int>& n, vector<int>& m, int left , int mid, int right)合并
    2. {
    3. //左边第一个元素
    4. int x = left;
    5. //右边第一个元素
    6. int y = mid + 1;
    7. //记录临时数组下标
    8. int z = left;
    9. while (x <= mid && y <= right)
    10. {
    11. if (n[x] < n[y])
    12. {
    13. m[z++] = n[x++];
    14. }
    15. else
    16. {
    17. m[z++] = n[y++];
    18. }
    19. }
    20. //左边还有剩余元素
    21. while (x <= mid)
    22. {
    23. m[z++] = n[x++];
    24. }
    25. //右边还有剩余元素
    26. while (y <= right)
    27. {
    28. m[z++] = n[y++];
    29. }
    30. //把数据复制给数组
    31. while (left <= right)
    32. {
    33. n[left] = m[left];
    34. left++;
    35. }
    36. }
    37. void msort(vector<int>& n, vector<int>& m, int left, int right)
    38. {
    39. if (left < right)
    40. {
    41. int mid = left + (right - left) / 2;//取中间点
    42. //递归拆分左边
    43. msort(n, m, left, mid);
    44. //递归拆分右边
    45. msort(n, m, mid + 1, right);
    46. //合并
    47. merge(n,m,left,mid,right);
    48. }
    49. }
    50. void merge_Sort(vector<int>&p,int n)//归并排序
    51. {
    52. vector<int>G(n, 0);//辅助数组
    53. msort(p, G, 0, n - 1);//归并入口
    54. }

     桶排序:

    类似于哈希表,解决冲突的方法为地址法,把每个头节点当作一个桶。

    把数据分别放入对应的桶中,然后从第一个桶开始,取数据放到数组中

    基数排序:

    1. 取得数组中的最大数,并取得位数。
    2. arr为原始数组,从最低位开始取每个位组成radix数组。
    3. 对radix进行计数排序(利用计数排序适用于小范围数的特点)

  • 相关阅读:
    ns2无线局域网隐藏节点仿真实验
    LGBM 模型保存为PMML 文件
    PaddleOCR mac 安装指南
    生产依赖与开发依赖区别: 前端程序没有区别,后端程序有点区别
    设计模式 - 责任链模式
    redis相关文章汇总
    Polygon zkEVM可验证计算简单状态机示例
    浅析新标准下数据中心行业应急照明和疏散指示系统的设计与产品选型
    景联文科技数据标注:人体关键点标注用途及各点的位置定义
    国产CPU对比
  • 原文地址:https://blog.csdn.net/qq_45303986/article/details/127458290