• 【数据结构】堆排序的实现



    目录

    1.向上调整算法 O(N*logN)

    2.向下调整算法 O(N)

    3.堆排序 O(N*logN)

    3.1比较建堆排序和直接堆排序

    3.2堆排序思想:

    3.2.1.首先在a这个数组中直接建堆

    3.2.2 排升序用大堆,降序用小堆

    3.3堆排序完整代码

    4.堆排序中建堆的时间复杂度的证明



    堆是一个完全二叉树

    假设在这个堆中高度为K层,总节点数为N

    那么由 2^K-1=N 得到 log(N+1)=K 


    1.向上调整算法 O(N*logN)

    在堆插入数据时使用

    1. //向上调整
    2. void AdjustUp(int* a, int child)//从孩子的位置向上调整
    3. {
    4. int parent = (child - 1) / 2;
    5. //请注意(-1)/2=0 所以不能用parent>=0作为判断方式
    6. while (child > 0)//child到0就不用交换了
    7. {
    8. if (a[parent] > a[child])
    9. {
    10. swap(&a[parent], &a[child]);
    11. child = parent;
    12. parent = (child - 1) / 2;
    13. }
    14. else
    15. {
    16. break;
    17. }
    18. }
    19. }

    2.向下调整算法 O(N)

    在堆删除数据的时候使用,本质是找出次大或者次小

    前提是左右子树都需要同时是大堆或者小堆

    1. void AdjustDown(int* a, int size,int parent)//从父亲的位置向下调整
    2. {
    3. assert(a);
    4. int minchild = parent * 2 + 1;
    5. while (minchild < size)
    6. {
    7. if (minchild + 1 < size)
    8. {
    9. if (a[minchild] > a[minchild + 1])//找到较小的子节点
    10. {
    11. minchild = minchild + 1;
    12. }
    13. }
    14. if (a[parent] > a[minchild])//条件满足,和较小的子节点交换位置
    15. {
    16. swap_(&a[parent], &a[minchild]);
    17. parent = minchild;
    18. minchild = parent * 2 + 1;
    19. }
    20. else
    21. {
    22. break;
    23. }
    24. }
    25. }

    3.堆排序 O(N*logN)

    3.1比较建堆排序和直接堆排序

    前提:这个排序需要让数组本身有序。

    如果直接使用实现的堆这个数据结构空间复杂度为O(N)

    建堆排序——————————————————

    1.建堆排序不是对这个数组处理,是会在堆中会额外开辟一片空间,从这个数组中拿数据。

    可以写一个循环每次拿一个。

    2.然后在其中建好堆之后(请注意堆中的数据不是有序的),开始取堆顶的数据,取堆顶数据时,方法需要把堆顶数据和最后一个数据进行交换,取最后一个数据,然后对于现在的第一个元素利用向下调整算法,重新调整。

    3.这样每次堆顶的数据都是比较前一个次小或者次大的,需要这样依次取出按顺序放入原先数组中,才能完成排序。

    堆排序——————————————————

    实际当中堆排序,会传一个数组给这个排序函数,不会额外开辟一片空间。

    void HeapSort(int* a, int n);
    

    3.2堆排序思想:

    把这个数组本身转化成一个堆,利用堆的性质进行排序。

    3.2.1.首先在a这个数组中直接建堆

    1.向下调整建堆O(N)————————————

            向下调整有前提,必须左右子树都是小堆或者大堆才可以。可以从倒数第一个非叶节点(就是最后一个节点的父节点),开始向下调整建堆。

      根据父节点的下标关系,调整完一个后下标--,找到前一个父节点,继续进行向下调整,以此类推。

    如下所示:

    1. //建堆 向下调整
    2. for (parent = (n - 1 - 1) / 2; parent >= 0; parent--)
    3. {
    4. AdjustDown_(a, n, parent);
    5. }

    2.向上调整建堆O(N*logN)———————————

      利用直接插入的思想,模拟插入过程进行尾插+向上调整。

    1. int child = 0;
    2. for (child = 0; child < n; child++)
    3. {
    4. AdjustUp(a, child);
    5. }


    3.2.2 排升序用大堆,降序用小堆

    堆排序本质是一种选择排序,找到最大的元素之后要考虑如何选次小的元素。

    堆排序时要利用堆排序本身的优势,它的父子节点的关系。

    以降序为例:

    1.建小堆

    2.第一个和最后一个元素进行位置交换,把最后一个元素不看做堆中的元素向下调整,选出次小的,以此类推

    1. //以排降序为例
    2. //交换位置,最小放最后,然后向下调整变成小根堆
    3. for (int i = 0; i < n; i++)
    4. {
    5. int last = n -1 - i;
    6. swap(&a[0], &a[last]);//交换第一个元素和最后一个元素
    7. AdjustDown(a, last, 0);//向下调整
    8. }

    3.3堆排序完整代码

    1. #include
    2. #include
    3. //交换
    4. void swap_(int* a, int* b)
    5. {
    6. int tmp = 0;
    7. tmp = *a;
    8. *a = *b;
    9. *b = tmp;
    10. }
    11. void AdjustDown_(int* a, int size,int parent)//从孩子的位置向下调整
    12. {
    13. assert(a);
    14. int minchild = parent * 2 + 1;
    15. while (minchild < size)
    16. {
    17. if (minchild + 1 < size)
    18. {
    19. if (a[minchild] > a[minchild + 1])//找到较小的子节点
    20. {
    21. minchild = minchild + 1;
    22. }
    23. }
    24. if (a[parent] > a[minchild])//条件满足,和较小的子节点交换位置
    25. {
    26. swap_(&a[parent], &a[minchild]);
    27. parent = minchild;
    28. minchild = parent * 2 + 1;
    29. }
    30. else
    31. {
    32. break;
    33. }
    34. }
    35. }
    36. // 对数组进行堆排序
    37. void HeapSort(int* a, int n)
    38. {
    39. //向下调整排序
    40. //排降序,小根堆
    41. int parent = 0;
    42. //建堆 向下调整
    43. for (parent = (n - 1 - 1) / 2; parent >= 0; parent--)
    44. {
    45. AdjustDown_(a, n, parent);
    46. }
    47. //交换位置,最小放最后,然后向下调整变成小根堆
    48. for (int i = 0; i < n; i++)
    49. {
    50. int last = n -1 - i;
    51. swap_(&a[0], &a[last]);
    52. AdjustDown_(a, last, 0);
    53. }
    54. }

    4.堆排序中建堆的时间复杂度的证明

    满二叉树的结构来计算


    4.1.向下调整建堆 O(N)


    4.2.向上调整建堆 O(N*logN)


    你好,这里是媛仔与难搞的堆排序……整理下来感觉还是对于堆排序不够熟练,还是要多多练习啊。希望这篇文章对你能够有所帮助,也欢迎和我多多交流!!我们下一篇见~

  • 相关阅读:
    【ESP-IDF篇】搭建ESP-IDF软件开发环境,包括手动命令行和VSCode两种方式
    mysql利用mysqldump方式搭建主从
    支付模块-微信支付
    Unix系统的进程相关操作
    《算法导论》15.4最长公共子序列(含C++代码)
    Java搭建企业级ERP架构学习(一)
    Microsoft SQL Server数据库语言及功能使用(十二)
    (区别、详解、使用)module.exports与exports,export与export default,import 与require
    vue 使用cornerstone解析 .dcm 文件
    稳定好用的短连接生成平台,支持API批量生成
  • 原文地址:https://blog.csdn.net/vpurple_/article/details/126233090