• 树--堆和优先权队列


    数据结构中的堆和栈与操作系统内存划分中的堆和栈没有关系

    一、堆的定义

    一个大小为n的堆是一棵包含n个结点的完全二叉树,其根节点称为堆顶。

    根据堆中亲子结点的大小关系,分为大堆和小堆:
    (1)最小堆:树中的每个结点的元素都小于或等于其孩子结点的元素。最小堆中堆顶存储的元素为整棵树中最小的

    (2)最大堆:树中的每个结点的元素都大于或等于其孩子结点的元素。最大堆中堆顶存储的元素为整棵树中最大的

    下列关键字序列为堆的是:(A

    A        100,60,70,50,32,65

    B        60,70,65,50,32,100

    C        65,100,70,32,50,60

    D        70,65,100,32,50,60

    E        32,50,100,70,65,60

    F        50,100,70,65,60,32

    二、堆的存储表示

    所有的数组都可以表示成完全二叉树,但是不是所有的数组或完全二叉树都是堆

    堆的逻辑结构:逻辑结构为完全二叉树

    堆的物理结构:实际上的存储结构是数组

    三、建堆算法的实现

    最大堆向上调整思路:

    向上调整代码实现:

    1. //size为数组大小,child为新插入的数据位置
    2. void AdjustUp(int *a,int size,int child)
    3. {
    4. assert(a);
    5. int parent = (child - 1) / 2;
    6. while (child>0)
    7. {
    8. //大堆
    9. //if (a[child] > a[parent])
    10. //小堆
    11. if(a[child]
    12. {
    13. //父子数值交换
    14. Swap(&a[child], &a[parent]);
    15. //更新父子结点
    16. child = parent;
    17. parent = (child - 1) / 2;
    18. }
    19. else
    20. {
    21. break;
    22. }
    23. }
    24. }

    最小堆向下调整思路:

    即将其调整成最小堆,向下调整,将根结点与左右孩子中更小的那个交换

    结束条件:①父亲<=小的孩子,则停止②调整至叶子结点

    向下调整代码实现:

    1. void AdjustDown(int *a,int size,int parent)
    2. {
    3. assert(a);
    4. int child = parent * 2 + 1;//左孩子
    5. while (child
    6. {
    7. //选更大的孩子结点,child+1为右孩子
    8. //if (child+1a[child])
    9. //选更小的孩子节点
    10. if(child+11]
    11. {
    12. child++;//如果右孩子值更大/小,那么child指向右孩子
    13. }
    14. //大堆:如果大的孩子大于父亲则交换
    15. //if (a[child]>a[parent])
    16. //小堆,如果小的孩子小于父亲则交换
    17. if(a[child]
    18. {
    19. Swap(&a[child], &a[parent]);
    20. parent = child;
    21. child = parent * 2 + 1;
    22. }
    23. else
    24. {
    25. break;
    26. }
    27. }
    28. }

    建堆算法实现思路:

    这里建堆采用向上调整,删除采用向下调整。

    删除操作采取数据交换的原因,是为了避免直接删除堆顶数据,导致整个数后面顺序全部改变。

    接口函数中的Push与Pop操作的时间复杂度为O(logN)

    建堆操作时间复杂度为O(N):因为叶子节点不需要堆化,所以需要堆化的节点从倒数第二层开始。每个节点堆化的过程中,需要比较和交换的节点个数,跟这个节点的高度 k 成正比。

    建堆算法代码实现:

    头文件:

    1. #pragma
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. typedef int HPDataType;
    9. typedef struct heap
    10. {
    11. int* a;
    12. int size;
    13. int capacity;
    14. }HP;
    15. void HeapCreate(HP* hp, HPDataType* a, int n);//构建一个堆
    16. void HeapPrintf(HP* hp);//循环输出打印堆中元素
    17. void Swap(HPDataType* px,HPDataType *py);
    18. void AdjustUp(int* a, int size, int child);//向上调整
    19. void AdjustDown(int* a,int size,int parent);//向下调整
    20. void HeapInit(HP* hp);//堆的初始化
    21. void HeapDestory(HP* hp);//堆销毁
    22. void HeapPush(HP* hp,HPDataType x);//堆中插入数据
    23. void HeapPop(HP* hp);//删除堆顶数据
    24. HPDataType HeapTop(HP* hp);//获取堆顶数据
    25. int HeapSize(HP* hp);//堆节点数量
    26. bool HeapEmpty(HP* hp);//判空
    27. void HeapSort(int* a, int n);//堆排序

    源文件: 

    1. #include"heap.h"
    2. void HeapCreate(HP* hp,HPDataType *a,int n)
    3. {
    4. assert(hp);
    5. hp->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
    6. if (hp->a==NULL)
    7. {
    8. perror("malloc fail");
    9. exit(-1);
    10. }
    11. memcpy(hp->a, a, sizeof(HPDataType) * n);
    12. //void *memcpy(void *str1, const void *str2, size_t n)
    13. //从存储区 str2 复制 n 个字节到存储区 str1。
    14. hp->size = hp->capacity = n;
    15. HeapSort(a, n);
    16. }
    17. void Swap(HPDataType* px,HPDataType* py)
    18. {
    19. HPDataType* tmp = *px;
    20. *px = *py;
    21. *py = tmp;
    22. }
    23. void HeapInit(HP* hp)
    24. {
    25. assert(hp);
    26. hp->a = NULL;
    27. hp->size = hp->capacity = 0;
    28. }
    29. void HeapDestory(HP* hp)
    30. {
    31. assert(hp);
    32. free(hp->a);
    33. hp->a = NULL;
    34. hp->size = hp->capacity = 0;
    35. }
    36. //向上调整
    37. //size为数组大小,child为新插入的数据位置
    38. void AdjustUp(int *a,int size,int child)
    39. {
    40. assert(a);
    41. int parent = (child - 1) / 2;
    42. while (child>0)
    43. {
    44. //大堆
    45. //if (a[child] > a[parent])
    46. //小堆
    47. if(a[child]
    48. {
    49. //父子数值交换
    50. Swap(&a[child], &a[parent]);
    51. //更新父子结点
    52. child = parent;
    53. parent = (child - 1) / 2;
    54. }
    55. else
    56. {
    57. break;
    58. }
    59. }
    60. }
    61. //堆插入数据堆其他结点没有影响
    62. //可能会影响它到根节点的路径上的结点关系
    63. void HeapPush(HP* hp, HPDataType x)
    64. {
    65. assert(hp);
    66. if (hp->size==hp->capacity)
    67. {
    68. int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
    69. HPDataType* tmp = realloc(hp->a, sizeof(HPDataType) * newcapacity);
    70. if (tmp==NULL)
    71. {
    72. printf("realloc fail\n");
    73. exit(-1);
    74. }
    75. hp->a = tmp;
    76. hp->capacity = newcapacity;
    77. }
    78. hp->a[hp->size] = x;
    79. hp->size++;
    80. AdjustUp(hp->a, hp->size, hp->size - 1);
    81. }
    82. void AdjustDown(int *a,int size,int parent)
    83. {
    84. assert(a);
    85. int child = parent * 2 + 1;//左孩子
    86. while (child
    87. {
    88. //选更大的孩子结点,child+1为右孩子
    89. //if (child+1a[child])
    90. //选更小的孩子节点
    91. if(child+11]
    92. {
    93. child++;//如果右孩子值更大/小,那么child指向右孩子
    94. }
    95. //大堆:如果大的孩子大于父亲则交换
    96. //if (a[child]>a[parent])
    97. //小堆,如果小的孩子小于父亲则交换
    98. if(a[child]
    99. {
    100. Swap(&a[child], &a[parent]);
    101. parent = child;
    102. child = parent * 2 + 1;
    103. }
    104. else
    105. {
    106. break;
    107. }
    108. }
    109. }
    110. //再进行向下调整算法
    111. void HeapPop(HP* hp)
    112. {
    113. assert(hp);
    114. assert(hp->size > 0);
    115. Swap(&hp->a[0], &hp->a[hp->size - 1]);//将堆顶数据与数组中最后一个数据交换
    116. hp->size--;//删除数组中的最后一个数据
    117. AdjustDown(hp->a, hp->size,0);//传入数组,数组的大小,向下调整起始位置
    118. }
    119. void HeapPrintf(HP* hp)
    120. {
    121. for(int i=0;isize;i++)
    122. {
    123. printf("%d ",hp->a[i]);
    124. }
    125. printf("\n");
    126. }
    127. HPDataType HeapTop(HP* hp)
    128. {
    129. assert(hp);
    130. assert(hp->size > 0);
    131. return hp->a[0];
    132. }
    133. int HeapSize(HP* hp)
    134. {
    135. return hp->size;
    136. }
    137. bool HeapEmpty(HP* hp)
    138. {
    139. assert(hp);
    140. return hp->size == 0;
    141. }

     四、堆排序

     也就是将堆中的数据进行排序,原本堆是一个完全二叉树。堆排序的目的是将结点中的数字,按照从小到大,或者从大到小的方式存储在数组中。

    也就是说如果有一个大堆,那么堆顶数据原本储在数组a[0]位置处。数组a中的数字存储顺序是无序的。堆排序的意义就是将数组中的按照一定的顺序排列。

     

    堆排序包括建堆和排序两个操作,建堆过程的时间复杂度是 O(n),排序过程的时间复杂度是 O(nlogn),所以,堆排序整体的时间复杂度是 O(nlogn)。

    堆排序代码实现:

    1. //向上调整
    2. //size为数组大小,child为新插入的数据位置
    3. void AdjustUp(int *a,int size,int child)
    4. {
    5. assert(a);
    6. int parent = (child - 1) / 2;
    7. while (child>0)
    8. {
    9. //大堆
    10. //if (a[child] > a[parent])
    11. //小堆
    12. if(a[child]
    13. {
    14. //父子数值交换
    15. Swap(&a[child], &a[parent]);
    16. //更新父子结点
    17. child = parent;
    18. parent = (child - 1) / 2;
    19. }
    20. else
    21. {
    22. break;
    23. }
    24. }
    25. }
    26. void AdjustDown(int *a,int size,int parent)
    27. {
    28. assert(a);
    29. int child = parent * 2 + 1;//左孩子
    30. while (child
    31. {
    32. //选更大的孩子结点,child+1为右孩子
    33. //if (child+1a[child])
    34. //选更小的孩子节点
    35. if(child+11]
    36. {
    37. child++;//如果右孩子值更大/小,那么child指向右孩子
    38. }
    39. //大堆:如果大的孩子大于父亲则交换
    40. //if (a[child]>a[parent])
    41. //小堆,如果小的孩子小于父亲则交换
    42. if(a[child]
    43. {
    44. Swap(&a[child], &a[parent]);
    45. parent = child;
    46. child = parent * 2 + 1;
    47. }
    48. else
    49. {
    50. break;
    51. }
    52. }
    53. }
    54. void HeapSort(int*a ,int n)
    55. {
    56. //把a构建成堆
    57. for (int i = 0;i
    58. {
    59. AdjustUp(a, n, i);
    60. }
    61. //降序排序
    62. // for (int i=(n-1-1)/2;i>=0;i--)
    63. // {
    64. // AdjustDown(a, n, i);
    65. // }
    66. }

  • 相关阅读:
    2023/09/19 qt day3
    RK3568开发笔记(十):开发板buildroot固件移植开发的应用Demo,启动全屏显示
    iOS脱壳之frida-ios-dump
    应用层协议 HTTP
    设计模式——访问者模式(Visitor Pattern)+ Spring相关源码
    干货,某大厂小姐姐深夜让我说出了秘密-springboot发邮件 原创
    如何实现快速的批量抓取拼多多商品数据?(包含价格销量详情等)
    3分钟使用 WebSocket 搭建属于自己的聊天室(WebSocket 原理、应用解析)
    在命令行中使用 cl.exe编译 C/C++ 程序并执行
    JSP基于Javaweb学籍管理系
  • 原文地址:https://blog.csdn.net/RXY24601/article/details/127794409