• 数据结构-二叉树-堆


    一、物理结构和逻辑结构

    在内存中的存储结构,逻辑结构为想象出来的存储结构。

    二、完全二叉树的顺序存储结构

    parent = (child - 1)/2

    leftchild = 2*parent + 1;

    rightchild = 2*parent +2

    上面的顺序结构只适合存储完全二叉树。如果存储,会浪费很多的空间。

    三、堆

    1、堆的分类

    小根堆:树中所有的父亲都小于或等于孩子。

    大根堆:树中所有的父亲都大于或等于孩子。

    接下来我们需要定义一个堆。定义过程如下:

    创建堆的时候会涉及到一个向上调整的算法:我们可以画图表示这一过程

    1. void UpAdjust(HPDataType* a, int child)
    2. {
    3. assert(a);
    4. int parent = (child - 1) / 2;
    5. //建立大堆
    6. while (child > 0)
    7. {
    8. if (a[child] > a[parent])
    9. {
    10. //交换两个数字
    11. Swap(&a[child], &a[parent]);
    12. child = parent;
    13. parent = (child - 1) / 2;
    14. }
    15. else
    16. {
    17. break;
    18. }
    19. }
    20. }

    删除堆顶数据需要涉及到向下调整

    这里不能挪动数据。原因有两个,效率低下,父子兄弟关系全乱了。

    思路就是:把头部数据和尾部数据交换。删除尾部数据,然后进行向下调整

    这样删除的优点是 效率高,保持了大部分堆的父子关系。

    向下调整的过程中我们需要和儿子中最大的进行比较。这样才能保证堆的关系不变。

    没有孩子时结束,转换一下就是child < size。

    1. void DownAdjust(HPDataType* a,int parent,int size)
    2. {
    3. int child = 2 * parent + 1;
    4. while (child < size)
    5. {
    6. //选出最大的那一个孩子
    7. if (child + 1 < size && a[child] < a[child + 1])
    8. {
    9. child++;
    10. }
    11. if (a[child] > a[parent])
    12. {
    13. //交换两个数字
    14. Swap(&a[child], &a[parent]);
    15. parent = child;
    16. child = 2 * parent + 1;
    17. }
    18. else
    19. {
    20. break;
    21. }
    22. }
    23. }

    整体实现

    1. #include "heap.h"
    2. void HeapInit(HP* hp)
    3. {
    4. assert(hp);
    5. hp->a = (HPDataType*)malloc(sizeof(HPDataType) * SIZE);
    6. hp->size = 0;
    7. hp->capacity = SIZE;
    8. }
    9. void AddCapacity(HP* hp)
    10. {
    11. assert(hp);
    12. if (hp->size == hp->capacity)
    13. {
    14. HPDataType* temp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * hp->capacity * 2);
    15. if (temp == NULL)
    16. {
    17. perror("realloc failed");
    18. return;
    19. }
    20. hp->a = temp;
    21. hp->capacity *= 2;
    22. }
    23. }
    24. void Swap(int* left, int* right)
    25. {
    26. int temp = *left;
    27. *left = *right;
    28. *right = temp;
    29. }
    30. //除了child的位置,前面的数据构成堆
    31. void UpAdjust(HPDataType* a, int child)
    32. {
    33. int parent = (child - 1) / 2;
    34. //建立大堆
    35. while (child > 0)
    36. {
    37. if (a[child] > a[parent])
    38. {
    39. //交换两个数字
    40. Swap(&a[child], &a[parent]);
    41. child = parent;
    42. parent = (child - 1) / 2;
    43. }
    44. else
    45. {
    46. break;
    47. }
    48. }
    49. }
    50. void HeapPush(HP* hp, HPDataType x)
    51. {
    52. //考虑扩容的问题
    53. AddCapacity(hp);
    54. //插入数据
    55. hp->a[hp->size++] = x;
    56. //还需要考虑向上调整的问题。
    57. UpAdjust(hp->a, hp->size - 1);
    58. }
    59. void DownAdjust(HPDataType* a,int parent,int size)
    60. {
    61. int child = 2 * parent + 1;
    62. while (child < size)
    63. {
    64. //选出最大的那一个孩子
    65. if (child + 1 < size && a[child] < a[child + 1])
    66. {
    67. child++;
    68. }
    69. if (a[child] > a[parent])
    70. {
    71. //交换两个数字
    72. Swap(&a[child], &a[parent]);
    73. parent = child;
    74. child = 2 * parent + 1;
    75. }
    76. else
    77. {
    78. break;
    79. }
    80. }
    81. }
    82. //删除头部的数据
    83. void HeapPop(HP* hp)
    84. {
    85. assert(hp);
    86. assert(!HeapEmpty(hp));
    87. //首先交换头和尾的数字
    88. Swap(&hp->a[0], &hp->a[hp->size-1]);
    89. //然后删除尾的数字
    90. hp->size--;
    91. //向下调整恢复堆的原型 向下调整的左右子树一定是堆
    92. DownAdjust(hp->a, 0, hp->size);
    93. }
    94. HPDataType HeapTop(HP* hp)
    95. {
    96. assert(hp);
    97. return hp->a[0];
    98. }
    99. bool HeapEmpty(HP* hp)
    100. {
    101. assert(hp);
    102. return hp->size == 0;
    103. }
    104. int HeapSize(HP* hp)
    105. {
    106. assert(hp);
    107. return hp->size;
    108. }

    四、堆排

    对数组进行排序。我们可以把它直接搞成一个堆,建堆操作

    1、向上调整建堆

    (1)把第一个数看成一个堆中的数。后来的数进行向上调整建立堆。 O(nlogn)

    (2)排升序需要建大堆,减小堆关系就都乱了

    利用向上调整建大堆时我们可以交换堆头和堆尾的值。然后在进行向下调整选出次小的值,如此往复。

    堆排的过程如下

    堆排代码:

    1. void HeapSort(int* a,int n)
    2. {
    3. //首先建立大堆
    4. for (int i = 1; i < n; i++)
    5. {
    6. UpAdjust(a, i);
    7. }
    8. //交换堆头和堆尾的数字选出最大的数字放到堆尾
    9. //然后向下调整
    10. int end = n - 1;
    11. while (end > 0)
    12. {
    13. Swap(&a[end], &a[0]);
    14. DownAdjust(a, 0, end);
    15. end--;
    16. }
    17. }

  • 相关阅读:
    ROS学习笔记(四)---使用 VScode 启动launch文件运行多个节点
    即时通讯技术文集(第21期):后端架构设计基础入门系列 [共15篇]
    My Ninety-second - 最长重复子数列 - By Nicolas
    5.3-5.4二分搜索算法实例
    SpringBoot——》集成Kafka示例
    Spring+SpringMVC+Mybatis(开发必备技能)01、基础idea环境配置
    Linux环境详解
    IOS手机耗电量测试
    一、项目整合管理
    VS Code结构体无法正确引出成员变量
  • 原文地址:https://blog.csdn.net/m0_67635008/article/details/138088783