当前位置:   article > 正文

[数据结构]——二叉树——堆的实现

[数据结构]——二叉树——堆的实现

1. 堆的概念及结构

如果有一个关键码的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储
在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0,1,
2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

2. 堆的实现

1.堆的创建


下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

int a[] = {1,5,3,8,7,6};

2. 建堆时间复杂度


因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果):

因此:建堆的时间复杂度为O(N)。 

3.堆的插入


先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。

 4.堆的删除


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

 5.堆向下调整算法

                                                                                                


现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

 3.代码深度解析

1.首先弄一个交换数据

 交换两个指针所指向的变量的值

通过解引用操作符*,将p2指针所指向的变量的值赋给了p1指针所指向的变量,将之前存储在temp中的值赋给了p2指针所指向的变量,完成了交换。

  1. void Swap(HPDataType *p1, HPDataType *p2)
  2. {
  3. HPDataType temp = *p1;
  4. *p1 =*p2;
  5. *p2 = temp;
  6. }

2.向上调整堆

将数组a中指定索引child的元素向上调整,使其在最小堆中满足最小堆的性质

通过计算child的父节点索引,即(parent = (child - 1) / 2),确定了父节点的位置。在循环中,child的元素比父节点的元素小调用Swap函数,将child和parent指向的元素进行交换。然后,代码更新child和parent的值,将child变为parent,parent变为(child - 1) / 2,继续循环。如果不是,即child的元素不小于父节点的元素,代码通过break语句跳出循环,这时已经完成了向上调整的操作。

  1. void AdjudtUp(HPDataType* a, int child)
  2. {
  3. int parent = (child - 1) / 2;
  4. while (child > 0)
  5. {
  6. if (a[child] < a[parent])
  7. {
  8. Swap(&a[child], &a[parent]);
  9. child = parent;
  10. parent = (child - 1) / 2;
  11. }
  12. else
  13. {
  14. break;
  15. }
  16. }
  17. }

3.向下调整堆

调整小顶堆的算法,接受一个int类型的指针a,表示一个数组,size表示数组的大小, parent表示要调整的节点位置

  1. void AdjustDown(int* a, int size, int parent)
  2. {
  3. int child = parent * 2 + 1;
  4. while (child < size)
  5. {
  6. if (child + 1 < size && a[child + 1] < a[child])
  7. {
  8. ++child;
  9. }
  10. if (a[child] < a[parent])
  11. {
  12. Swap(&a[child], &a[parent]);
  13. parent = child;
  14. child = parent * 2 + 1;
  15. }
  16. else
  17. {
  18. break;
  19. }
  20. }
  21. }

4.堆的插入

堆已满,则需要扩容。然后,将原来的内存空间指针hp->_a指向新分配的内存空间,更新堆的容量为新的容量。将要插入的元素x赋值给堆的最后一个位置hp->_a[hp->_size],然后增加堆的大小hp->_size++。最后,调用AdjustUp函数将新插入的元素向上调整,以维护堆的性质

  1. void HeapPush(Heap* hp, HPDataType x)
  2. {
  3. assert(hp);
  4. if (hp->_size == hp->_capacity)
  5. {
  6. int newCapacity = hp->_capacity == 0 ? 4: hp->_capacity * 2;
  7. HPDataType*temp = (HPDataType*)realloc(hp->_a, newCapacity * sizeof(HPDataType));
  8. if (temp == NULL)
  9. {
  10. perror("realloc fail");
  11. exit(-1);
  12. }
  13. hp->_a =temp;
  14. hp->_capacity = newCapacity;
  15. }
  16. hp->_a[hp->_size] = x;
  17. hp->_size++;
  18. AdjudtUp(hp->_a,hp->_size-1);
  19. }

5.堆的创建

初始化后再通过for循环遍历数组a,并调用HeapPush函数将数组中的元素依次插入到堆中

  1. void HeapCreate(Heap* hp, HPDataType *a, int n )
  2. {
  3. assert(hp);
  4. hp->_size = 0;
  5. hp->_capacity =NULL;
  6. // 将数组a中的元素依次插入堆中
  7. for (int i = 0; i < n; i++) {
  8. HeapPush(hp, a[i]);
  9. }
  10. }

6.堆的销毁

使用free函数释放堆的数组hp->_a的内存空间

  1. void HeapDestory(Heap* hp)
  2. {
  3. assert(hp);
  4. free(hp->_a);
  5. hp->_a = NULL;
  6. hp->_size =hp->_capacity=0;
  7. }

7.堆的删除

调用AdjustDown()函数,从堆顶开始向下调整堆,以保持堆的性质

  1. void HeapPop(Heap* hp)
  2. {
  3. assert(hp);
  4. assert(hp->_size > 0);
  5. Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);
  6. hp->_size--;
  7. AdjustDown(hp->_a, hp->_size, 0);
  8. }

8.取堆顶的数据


函数返回堆中数组_a的第一个元素即堆顶的数据

  1. 取堆顶的数据
  2. HPDataType HeapTop(Heap* hp)
  3. {
  4. assert(hp);
  5. assert(hp->_size > 0);
  6. return hp->_a[0];
  7. }

9.堆的数据个数

返回堆的_size成员,即堆的大小

  1. int HeapSize(Heap* hp)
  2. {
  3. assert(hp);
  4. return hp->_size;
  5. }



10. 堆的判空

  1. 通过断言assert(hp);来确保传入的参数hp不为NULL。

  2. 然后,通过判断堆的大小hp->_size是否为0来判断堆是否为空。

  3. 如果堆的大小为0,则返回1(即堆为空),否则返回0(即堆不为空)

  1. int HeapEmpty(Heap* hp)
  2. {
  3. assert(hp);
  4. return hp->_size == 0;
  5. }

4.总的代码

1.heap.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. #include<stdbool.h>
  6. typedef int HPDataType;
  7. typedef struct Heap
  8. {
  9. HPDataType* _a;
  10. int _size;
  11. int _capacity;
  12. }Heap;
  13. //数据交换
  14. void Swap(HPDataType* p1, HPDataType* p2);
  15. //向上堆的调整
  16. void AdjudtUp(Heap* _a, int child);
  17. // 向下调整堆
  18. void AdjustDown(int* a, int size, int parent);
  19. // 堆的构建
  20. //void HeapCreate(Heap* hp);//, HPDataType* a, int n
  21. void HeapCreate(Heap* hp, HPDataType* a, int n);
  22. // 堆的销毁
  23. void HeapDestory(Heap* hp);
  24. // 堆的插入
  25. void HeapPush(Heap* hp, HPDataType x);
  26. // 堆的删除
  27. void HeapPop(Heap* hp);
  28. // 取堆顶的数据
  29. HPDataType HeapTop(Heap* hp);
  30. // 堆的数据个数
  31. int HeapSize(Heap* hp);
  32. // 堆的判空
  33. int HeapEmpty(Heap* hp);

2.Heap.c

  1. #include"Heap.h"
  2. //交换数据
  3. void Swap(HPDataType *p1, HPDataType *p2)
  4. {
  5. HPDataType temp = *p1;
  6. *p1 =*p2;
  7. *p2 = temp;
  8. }
  9. //向上调整堆
  10. void AdjudtUp(HPDataType* a, int child)
  11. {
  12. int parent = (child - 1) / 2;
  13. while (child > 0)
  14. {
  15. if (a[child] < a[parent])
  16. {
  17. Swap(&a[child], &a[parent]);
  18. child = parent;
  19. parent = (child - 1) / 2;
  20. }
  21. else
  22. {
  23. break;
  24. }
  25. }
  26. }
  27. // 向下调整堆
  28. void AdjustDown(int* a, int size, int parent)
  29. {
  30. int child = parent * 2 + 1;
  31. while (child < size)
  32. {
  33. if (child + 1 < size && a[child + 1] < a[child])
  34. {
  35. ++child;
  36. }
  37. if (a[child] < a[parent])
  38. {
  39. Swap(&a[child], &a[parent]);
  40. parent = child;
  41. child = parent * 2 + 1;
  42. }
  43. else
  44. {
  45. break;
  46. }
  47. }
  48. }
  49. // 堆的插入
  50. void HeapPush(Heap* hp, HPDataType x)
  51. {
  52. assert(hp);
  53. if (hp->_size == hp->_capacity)
  54. {
  55. int newCapacity = hp->_capacity == 0 ? 4: hp->_capacity * 2;
  56. HPDataType*temp = (HPDataType*)realloc(hp->_a, newCapacity * sizeof(HPDataType));
  57. if (temp == NULL)
  58. {
  59. perror("realloc fail");
  60. exit(-1);
  61. }
  62. hp->_a =temp;
  63. hp->_capacity = newCapacity;
  64. }
  65. hp->_a[hp->_size] = x;
  66. hp->_size++;
  67. AdjudtUp(hp->_a,hp->_size-1);
  68. }
  69. // 堆的构建
  70. //void HeapCreate(Heap* hp)//HPDataType* a, int n
  71. //{
  72. // assert(hp);
  73. // hp->_size = 0; // 初始化堆的大小为0
  74. // hp->_capacity = 0; // 设置堆的容量为n
  75. // hp->_a = NULL;
  76. //
  77. // 将数组a中的元素依次插入堆中
  78. // //for (int i = 0; i < n; i++) {
  79. // // HeapPush(&hp, a[i]);
  80. // //}
  81. //}
  82. void HeapCreate(Heap* hp, HPDataType *a, int n )
  83. {
  84. assert(hp);
  85. hp->_size = 0; // 初始化堆的大小为0
  86. hp->_capacity =NULL; //
  87. hp->_a = NULL;
  88. // 将数组a中的元素依次插入堆中
  89. for (int i = 0; i < n; i++) {
  90. HeapPush(hp, a[i]);
  91. }
  92. }
  93. // 堆的销毁
  94. void HeapDestory(Heap* hp)
  95. {
  96. assert(hp);
  97. free(hp->_a);
  98. hp->_a = NULL;
  99. hp->_size =hp->_capacity=0;
  100. }
  101. // 堆的删除
  102. void HeapPop(Heap* hp)
  103. {
  104. assert(hp);
  105. assert(hp->_size > 0);
  106. Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);
  107. hp->_size--;
  108. AdjustDown(hp->_a, hp->_size, 0);
  109. }
  110. // 取堆顶的数据
  111. HPDataType HeapTop(Heap* hp)
  112. {
  113. assert(hp);
  114. assert(hp->_size > 0);
  115. return hp->_a[0];
  116. }
  117. // 堆的数据个数
  118. int HeapSize(Heap* hp)
  119. {
  120. assert(hp);
  121. return hp->_size;
  122. }
  123. // 堆的判空
  124. int HeapEmpty(Heap* hp)
  125. {
  126. assert(hp);
  127. return hp->_size == 0;
  128. }

2.Test.c

  1. #include"Heap.h"
  2. void test()
  3. {
  4. Heap sl;
  5. int a[10] = { 10,8,2,4,5,3,6,7,9,1 };
  6. /*HeapCreate(&sl);
  7. for (int i = 0;i < sizeof(a) / sizeof(int); i++)
  8. {
  9. HeapPush(&sl, a[i]);
  10. }*/
  11. HeapCreate(&sl,a, sizeof(a) / sizeof(int));
  12. while (!HeapEmpty(&sl)) {
  13. printf("%d ", HeapTop(&sl));
  14. HeapPop(&sl);
  15. }
  16. printf("\n");
  17. }
  18. int main()
  19. {
  20. test();
  21. return 0;
  22. }

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号