• 数据结构——堆(C语言)


    本篇会解决一下几个问题:
    1.堆是什么?
    2.如何形成一个堆?
    3.堆的应用场景
     

    堆是什么?

    • 堆总是一颗完全二叉树
    • 堆的某个节点总是不大于或不小于父亲节点

    如图,在小堆中,父亲节点总是小于孩子节点的。

    如图,在大堆中,父亲节点总是大于孩子节点的。

    堆和二叉树还是有很大区别的,堆是用数组来实现的,尽管逻辑结构上是一颗二叉树,但在内存上要比二叉树好,普通的二叉树,你要用链表来存储他们的左右孩子,还要给他们分配空间,但堆只是用数组来表示。

    如何形成一个堆?

    堆的创建有向上调整和向下调整两种方式。

    向上调整:从第一个非叶子节点开始向上调整,一直调整到根节点。

    用int a[] ={1,5,3,8,7,6};来做例子,

    如图所示,

    向下调整:从根节点开始,和左右孩子中小或者大的节点比较,交换,直到小于数组元素。

    堆的插入

    堆的删除

    删除堆是删除堆顶的元素,将堆顶的元素根据最后一个数据一换,然后删除数组中最后一个元素,再进行向下调整算法。

    这里想一想为什么要这样???

    1.因为堆是有数组来创建的,如果直接删除堆顶的数据,第一个缺点就是会造成移动,从后往前覆盖,这样就会造成一个问题。兄弟节点变成父子节点,而且这样也不能很好的利用数组的优点。

    2.如果是交换第一个和最后一个元素,这样有2个优点:

    • 第一个是不会破坏除了堆顶的左右堆的结构。
    • 第二个就是会利用数组的优点,数组读取速度很快,这样每次最后或最小的元素就放在了后面。

    堆的时间复杂度

    向下调整时间复杂度:

     则要移动节点的总步数为:

    向上调整时间复杂度:

    则要调整的节点总数为:

    堆的应用场景

    1. 堆排序,可以用堆的建立和堆的删除来实现排序,堆排序十分稳定(相同元素的相对位置不会发生交换),而且时间复杂度都是O(N*logN)
    2. TOP-K问题,我们想一想王者荣耀中前100的玩家是怎么实现的,或者专业前10名...问题

    1).先回答一下TOP-K问题:即求数据结合中前K个最大的元素或最小的元素,一把情况下数据很大。

    2).对于这种场景,首先想到的就是排序,但是:数据非常大,排序就不可取了,因为内存大小的原因,不会全部加载到内存,这时堆就发生了巨大的优势。

    思路:利用K个元素建堆,如果是求最大的K个元素,就建立小堆,求最小的K歌元素,就建立大堆。然后用N-K个元素与堆顶元素比较,满足条件就交换。

    下面是源码:

    1. void HeapInit(Heap* php)
    2. {
    3. assert(php);
    4. php->a = NULL;
    5. php->size = php->capacity =0;
    6. }
    7. void HeapDestroy(Heap* php)
    8. {
    9. assert(php);
    10. free(php->a);
    11. php->a = NULL;
    12. php->capacity = php->size =0;
    13. }
    14. void Swap(HeapDateType* child, HeapDateType* parent){
    15. HeapDateType tmp = *child;
    16. *child= *parent;
    17. *parent = tmp;
    18. }
    19. void AdjustUp(HeapDateType* a,int child){
    20. int parent = (child-1)/2;
    21. while(child > 0){
    22. if(a[child] < a[parent]){
    23. Swap(&a[child],&a[parent]);
    24. child = parent;
    25. parent = (child-1)/2;
    26. }else{
    27. break;
    28. }
    29. }
    30. }
    31. void HeapPush(Heap* php,HeapDateType x)
    32. {
    33. assert(php);
    34. if(php->size == php->capacity){
    35. int newCapacity = php->capacity == 0?4:php->capacity*2;
    36. HeapDateType* tmp = (HeapDateType*)realloc(php->a,sizeof(HeapDateType)*newCapacity);
    37. if(tmp == NULL){
    38. perror("realloc fail\n");
    39. }
    40. php->a = tmp;
    41. php->capacity = newCapacity;
    42. }
    43. php->a[php->size] = x;
    44. php->size++;
    45. AdjustUp(php->a,php->size-1);
    46. }
    47. void HeapPrint(Heap* php)
    48. {
    49. assert(php);
    50. for(size_t i =0; i<php->size; i++){
    51. std::cout << php->a[i] << " ";
    52. }
    53. std::cout << std::endl;
    54. }
    55. void AdjustDown(HeapDateType* a,int n, int parent)
    56. {
    57. int child = parent*2+1;
    58. while(child < n){
    59. if(child+1 < n && a[child+1] < a[child]){
    60. child++;
    61. }
    62. if(a[child] < a[parent]){
    63. Swap(&a[child],&a[parent]);
    64. parent = child;
    65. child = parent*2+1;
    66. }else{
    67. break;
    68. }
    69. }
    70. }
    71. HeapDateType HeapTop(Heap* php)
    72. {
    73. assert(php);
    74. assert(php->size > 0);
    75. return php->a[0];
    76. }
    77. void HeapPop(Heap* php)
    78. {
    79. assert(php);
    80. assert(php->size > 0);
    81. Swap(&php->a[0],&php->a[php->size-1]);
    82. --php->size;
    83. AdjustDown(php->a,php->size,0);
    84. }
    85. bool HeapEmpty(Heap* php)
    86. {
    87. assert(php);
    88. return php->size == 0;
    89. }

    1. void HeapSort(int* a, int n)
    2. {
    3. //向上调整 O(n*logn)
    4. // for(size_t i =1; i<n; i++){
    5. // AdjustUp(a,i);
    6. // }
    7. //
    8. //向下调整 O(n)
    9. for(int i = (n-2)/2; i>=0; i--){
    10. AdjustDown(a,n,i);
    11. }
    12. //时间复杂度O(N*logN)
    13. int end = n-1;
    14. while(end > 0){
    15. Swap(&a[0],&a[end]);
    16. AdjustDown(a,end,0);
    17. --end;
    18. }
    19. }
    20. void PrintTopK(const char* filename,int k)
    21. {
    22. FILE* fout = fopen(filename,"r");
    23. if(fout == NULL){
    24. perror("fopen fail");
    25. exit(-1);
    26. }
    27. int* minHeap = (int*)malloc(sizeof(int)*k);
    28. if(minHeap == NULL){
    29. perror("malloc fail");
    30. exit(-1);
    31. }
    32. for(int i =0; i<k; i++){
    33. fscanf(fout,"%d",&minHeap[i]);
    34. }
    35. for(int i = (k-2)/2; i>=0; i++){
    36. AdjustDown(minHeap,k,0);
    37. }
    38. int x =0;
    39. while(fscanf(fout,"%d",&x)!= EOF){
    40. if(x > minHeap[0]){
    41. minHeap[0] = x;
    42. AdjustDown(minHeap,k,0);
    43. }
    44. }
    45. for(int i =0; i<k; i++){
    46. std::cout << minHeap[i] << " ";
    47. }
    48. std::cout << std::endl;
    49. }

                            

  • 相关阅读:
    网络协议学习:虚拟路由冗余协VRRP
    乘法逆元的模板代码
    详解|一级建造师考试报名流程有哪些?
    Leetcode.965 单值二叉树
    《许犁庭与柔性世界》第十三章 伊拉斯蒂克
    [GStreamer] 定义并使用多参数信号
    NVR添加rtsp流模拟GB28181视频通道
    vue3【toRef和toRefs--详】
    [附源码]SSM计算机毕业设计时事资讯平台JAVA
    任务九 深度学习 K近邻学习
  • 原文地址:https://blog.csdn.net/m0_72165281/article/details/133440797