码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 数据结构学习笔记(第八章 排序-内部排序)


     

    目录

     插入排序

    直接插入排序

    核心思想

    代码示例

    折半插入查找

    核心思想

    代码示例

     希尔排序

    基本思想

    增量选取

    代码示例(王道书上的)

    交换排序

    冒泡排序

    核心思想

    代码示例

    快速排序

    核心思想

    代码示例

    选择排序

    简单选择排序

    核心思想

    示例代码

    堆排序

     核心思想

    代码示例

    归并排序和基数排序

    归并排序

    核心思想

    代码示例 

    基数排序

    定义

    总结(算法效率)


     插入排序

    主要针对已排好序的子序列。

    直接插入排序

    核心思想

    上面一组例子直观的表示了插入排序。核心思想也就是给定一组数据,插入一个排一次序。

    若后一个元素比前一个元素小,那么哨兵更新,为小的那个。总体来说思想较为简单。 

    代码示例

    1. //插入排序,从小到大排序,升序
    2. void InsertSort(ElemType A[],int n)
    3. {
    4. int i,j;
    5. //24 66 94 2 15 74 28 51 22 18 2
    6. for(i=2;i<=n;i++)//第零个元素是哨兵,从第二个元素开始拿,往前面插入
    7. {
    8. if(A[i]1])
    9. {
    10. A[0]=A[i];//放到暂存位置,A[0]即是暂存,也是哨兵
    11. for(j=i-1;A[0]//移动元素,内层循环控制有序序列中的每一个元素和要插入的元素比较
    12. A[j+1]=A[j];
    13. A[j+1]=A[0];//把暂存元素插入到对应位置
    14. }
    15. }
    16. }

    时间复杂度为o(n2),空间复杂度o(1),稳定

    适用:顺序存储和链式存储的线性表。

    折半插入查找

    核心思想

    主要就是利用折半查找找插入的位置

    例:定义一个升序的数组 a[6] = {1,35,55,66,78,99}。其中我要向数组插入47。

    1,35,55,66,78,99
    通过改良二分查找,可以得到 最终跳出循环后min = 2,那么47最终插入的位置就为下标 = 2的位置,原先插入位置的元素和后面的元素全部向后移动一位。

    所以最终的结果{1,35,47,55,66,78,99}

    代码示例

    1. //折半查找 插入排序
    2. void MidInsertSort(ElemType A[],int n)
    3. {
    4. int i,j,low,high,mid;
    5. for(i=2;i<=n;i++)
    6. {
    7. A[0]=A[i];
    8. low=1;high=i-1;
    9. while(low<=high)//先通过二分查找找到待插入位置
    10. {
    11. mid=(low+high)/2;
    12. if(A[mid]>A[0])
    13. high=mid-1;
    14. else
    15. low=mid+1;
    16. }
    17. for(j=i-1;j>=high+1;--j)
    18. A[j+1]=A[j];
    19. A[high+1]=A[0];
    20. }
    21. }

    时间复杂度o(n2),空间复杂度o(nlog2n),稳定

    适用于顺序存储

     希尔排序

    基本思想

    首先他的基本思想就是选取一个增量,根据增量进行分组排序,然后增量递减,直到为一,排序完成。

    思想较为简单,具体示例请看下图

     这个第一轮选取的增量为5,第二次为3,第三次为1。排序就完成了。

    若希尔排序还是不太理解的同学,可以看看这个博主写的

    希尔排序_天地高歌的博客-CSDN博客_希尔排序

    增量选取

    这里肯定有同学有疑问:增量到底是怎么选取的呀?

    下面给出一条大佬们得出的结论:

    1.增量必须递减,且最后增量为1(标志排序完成)。

    2.增量序列必须互质,不然最小增量不起作用。

    于是有些大佬就发明了一些求增量的方法,Hibbard增量序列、Knuth增量序列、Sedgewick增量序列等等

    我们书上用的是希尔增量。

    希尔增量
    最坏时间复杂度:O(n^2)
    教材上都是以希尔增量为例子 => [N/2, (N/2)/2, …]

    Hibbard增量
    最坏时间复杂度:O(n^1.5)
    增量序列:2^k-1 => [1, 3, 7, 15, …]

    如果想了解更多关于增量的知识,请看

    https://blog.csdn.net/Foliciatarier/article/details/53891144?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165891299416782390510402%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165891299416782390510402&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-53891144-null-null.142^v35^experiment_28w_v1,185^v2^tag_show&utm_term=Knuth%E5%A2%9E%E9%87%8F%E5%BA%8F%E5%88%97&spm=1018.2226.3001.4187icon-default.png?t=M666https://blog.csdn.net/Foliciatarier/article/details/53891144?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165891299416782390510402%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165891299416782390510402&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-53891144-null-null.142^v35^experiment_28w_v1,185^v2^tag_show&utm_term=Knuth%E5%A2%9E%E9%87%8F%E5%BA%8F%E5%88%97&spm=1018.2226.3001.4187

    代码示例(王道书上的)

    1. void ShellSort(ElemType A[],int n)
    2. {
    3. int dk,i,j;
    4. // 73 29 74 51 29 90 37 48 72 54 83
    5. for(dk=n/2;dk>=1;dk=dk/2)//步长变化
    6. {
    7. for(i=dk+1;i<=n;++i)//以dk为步长进行插入排序
    8. {
    9. if(A[i]
    10. {
    11. A[0]=A[i];
    12. for(j=i-dk;j>0&&A[0]
    13. A[j+dk]=A[j];
    14. A[j+dk]=A[0];
    15. }
    16. }
    17. }
    18. }

    空间复杂度o(1),最坏时间复杂度o(n2),不稳定
    仅适用于线性表顺序存储

    交换排序

    通过比较关键字来交换位置排序

    冒泡排序

    核心思想

    从前往后或从后往前,依次两两比较相邻元素,若不符合规则(顺序或逆序),则交换位置。

    代码示例

    1. void swap(ElemType &a,ElemType &b)
    2. {
    3. ElemType tmp;
    4. tmp=a;
    5. a=b;
    6. b=tmp;
    7. }
    8. // 64 94 95 79 69 84 18 22 12 78
    9. // 12 64 94 95 79 69 84 18 22 78
    10. void BubbleSort(ElemType A[],int n)
    11. {
    12. int i,j;
    13. bool flag;
    14. for(i=0;i-1;i++)//i最多访问到8
    15. {
    16. flag=false;
    17. for(j=n-1;j>i;j--)//把最小值就放在最前面
    18. {
    19. if(A[j-1]>A[j])
    20. {
    21. swap(A[j-1],A[j]);
    22. flag=true;
    23. }
    24. }
    25. if(false==flag)
    26. return;
    27. }
    28. }

     空间复杂度:o(1),平均和最坏时间复杂度:o(n2),稳定

    快速排序

    核心思想

    基于分治法,比如说我们要顺序的排序。我们去数组的一个分隔值,把比值小的都放值左边,反之则放右边,然后不断递归,最终分割的只剩一个元素,自然就得到有序序列了。

    图解法请参考这个博主

    快速排序法(详解)_李小白~的博客-CSDN博客_快速排序

    代码示例

    1. int Partition(int* arr, int left, int right)
    2. {
    3. int k, i;
    4. for (k = i = left;i
    5. {
    6. if (arr[i] < arr[right])
    7. {
    8. swap(arr[i], arr[k]);
    9. k++;
    10. }
    11. }
    12. swap(arr[k], arr[right]);
    13. return k;
    14. }
    15. //递归实现
    16. void QuickSort(ElemType A[],int low,int high)
    17. {
    18. if(low
    19. {
    20. int pivotpos=Partition(A,low,high);//分割点左边的元素都比分割点要小,右边的比分割点大
    21. QuickSort(A,low,pivotpos-1);
    22. QuickSort(A,pivotpos+1,high);
    23. }
    24. }

     最坏空间复杂度o(n),与递归调用的深度有关。最坏时间复杂度为o(n2),不稳定

    快速排列算法是内部排序算法中平均性能最优的算法。

    选择排序

    每次选择后面n-i+1的最小元素。

    简单选择排序

    核心思想

    与上面差不多,也就是每次选择n-i+1(i为i趟)后的最小元素与位置为i的元素交换。

    示例代码

    1. void swap(ElemType &a,ElemType &b)
    2. {
    3. ElemType tmp;
    4. tmp=a;
    5. a=b;
    6. b=tmp;
    7. }
    8. void SelectSort(ElemType A[],int n)
    9. {
    10. int i,j,min;//min记录最小的元素的下标
    11. for(i=0;i-1;i++)//最多可以为8
    12. {
    13. min=i;
    14. for(j=i+1;j//j最多可以为9
    15. {
    16. if(A[j]
    17. min=j;
    18. }
    19. if(min!=i)
    20. {
    21. swap(A[i],A[min]);
    22. }
    23. }
    24. }

    空间复杂度o(1),时间复杂度o(n2),不稳定

    堆排序

    分为大根堆和小根堆

    大根堆是最大元素放在根结点,,且任一非根结点的值小于等于其双亲结点,小根堆则与其定义相反。下面是大根堆示例图

     核心思想

    首先将元素建成初始堆,堆顶元素为最大值。输出堆顶元素后,通常将堆底元素送入堆顶,此时已不满足大根堆性质,所以我们需要将堆顶元素向下调整,反复重复,直到堆中仅剩一个元素。

    代码示例

    1. void swap(ElemType &a,ElemType &b)
    2. {
    3. ElemType tmp;
    4. tmp=a;
    5. a=b;
    6. b=tmp;
    7. }
    8. //调整某个父亲节点
    9. void AdjustDown(ElemType A[],int k,int len)
    10. {
    11. int i;
    12. A[0]=A[k];
    13. for(i=2*k;i<=len;i*=2)
    14. {
    15. if(i1])//左子节点与右子节点比较大小
    16. i++;
    17. if(A[0]>=A[i])
    18. break;
    19. else{
    20. A[k]=A[i];
    21. k=i;
    22. }
    23. }
    24. A[k]=A[0];
    25. }
    26. //用数组去表示树 层次建树
    27. void BuildMaxHeap(ElemType A[],int len)
    28. {
    29. for(int i=len/2;i>0;i--)
    30. {
    31. AdjustDown(A,i,len);
    32. }
    33. }
    34. void HeapSort(ElemType A[],int len)
    35. {
    36. int i;
    37. BuildMaxHeap(A,len);//建立大顶堆
    38. for(i=len;i>1;i--)
    39. {
    40. swap(A[i],A[1]);
    41. AdjustDown(A,1,i-1);
    42. }
    43. }

    空间复杂度:o(1),最坏和平均时间复杂度:nlog2n,不稳定

    归并排序和基数排序

    归并排序

    核心思想

    将两个或两个以上有序表组合成一个新的有序表

    代码示例 

    1. void Merge(ElemType A[],int low,int mid,int high)
    2. {
    3. ElemType B[N];
    4. int i,j,k;
    5. for(k=low;k<=high;k++)//复制元素到B中
    6. B[k]=A[k];
    7. for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++)//合并两个有序数组
    8. {
    9. if(B[i]<=B[j])
    10. A[k]=B[i++];
    11. else
    12. A[k]=B[j++];
    13. }
    14. while(i<=mid)//如果有剩余元素,接着放入即可
    15. A[k++]=B[i++];
    16. while(j<=high)
    17. A[k++]=B[j++];
    18. }
    19. //归并排序不限制是两两归并,还是多个归并
    20. // 1 3 5 7 9
    21. // 2 4
    22. // 1 2 3 4 5 7 9 主要的代码逻辑
    23. void MergeSort(ElemType A[],int low,int high)//递归分割
    24. {
    25. if(low
    26. {
    27. int mid=(low+high)/2;
    28. MergeSort(A,low,mid);
    29. MergeSort(A,mid+1,high);
    30. Merge(A,low,mid,high);
    31. }
    32. }

    二路归并排序空间复杂度o(n),时间复杂度o(nlog2n),稳定

    基数排序

    定义

     图解算法请看

    排序算法之基数排序_小C哈哈哈的博客-CSDN博客_基数排序算法

    空间复杂度o(r),r为辅助存储空间,辅助队列数量。时间复杂度o(d(n+r)),d为趟,r为辅助队列数量。稳定。

    总结(算法效率)

    以上均为内部排序算法。 

  • 相关阅读:
    Highlight Insights with Conditional
    无纸化办公小程序数据交互、wxs的使用
    微信小程序开发之map地理定位的使用
    百战RHCE(第四十七战:运维工程师必会技-Ansible学习2-Ansible安装配置练习环境)
    Linux操作文件的命令
    python爬虫入门到精通路线
    DHCP与TCP的简单解析
    使用JMeter进行接口压力测试
    秋招每日一题T32——安排电影院座位
    好用的翻译软件-大家都在用的互译软件
  • 原文地址:https://blog.csdn.net/weixin_53011574/article/details/125964731
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号