当前位置:   article > 正文

基于C语言实现堆的一些操作_c语言 利用数组实现堆的三种基本操作

c语言 利用数组实现堆的三种基本操作

堆是一个完全二叉树,堆分两种,一种为小堆,一种为大堆
小堆是指对于这个树的任意任意一个子树来说,子树的根节点都要小于左右孩子节点的值,小堆的根节点是这个树的最小元素
堆是指对于这个树的任意任意一个子树来说,子树的根节点都要大于左右孩子节点的值,大堆的根节点是这个树的最大元素
1.对堆进行初始化操作
其中有一个比较函数Compare,来比较确定是大堆还是小堆

  1. 16 //初始化,决定是大堆还是小堆
  2. 17 void HeapInit(Heap* heap,Compare cmp)
  3. 18 {
  4. 19 if(heap == NULL)
  5. 20 {
  6. 21 //非法输入
  7. 22 return ;
  8. 23 }
  9. 24 heap->size = 0;
  10. 25 heap ->cmp = cmp;
  11. 26 return ;
  12. 27 }
  1. 5 //为大堆打造的比较函数
  2. 6 int Greater(HeapType a,HeapType b)
  3. 7 {
  4. 8 return a>b?1:0;
  5. 9 }
  6. 10
  7. 11 //为小堆打造的比较函数
  8. 12 int Less(HeapType a,HeapType b)
  9. 13 {
  10. 14 return a<b?1:0;
  11. 15 }

2.销毁堆
注:在销毁堆操作的时候,不能释放内存,因为不是使用malloc来申请的空间
  1. 29 //销毁堆
  2. 30 //注:此时内存不能释放,因为没有malloc
  3. 31 void HeapDestroy(Heap* heap)
  4. 32 {
  5. 33 if(heap == NULL)
  6. 34 {
  7. 35 //非法输入
  8. 36 return ;
  9. 37 }
  10. 38 heap->size = 0;
  11. 39 heap->cmp = NULL;
  12. 40 return ;
  13. 41 }

3.向堆中插入元素
向堆中插入一个元素,因为堆是一种完全二叉树,那么就一定满足,插入的元素紧接在最后一个元素的后面,相当于是对数组进行尾插
以下面的堆为例,要将0.5插入到下面的堆中,首先先将要插入的元素0.5插入到原先的堆2的右子树上,由于该堆为小堆,这样的结果不满足小堆的定义,因此要将0.5“上浮”,即与此时位置的父节点交换位置,交换之后发现还是不满足小堆的定义,因此要再次“上浮”, 与此时位置的父节点交换位置,直到满足小堆的定义

首先先要对堆进行合法性判断,使用AdjustUp函数来辅助完成插入操作
  1. 72 void HeapInsert(Heap* heap ,HeapType value)
  2. 73 {
  3. 74 if(heap == NULL)
  4. 75 {
  5. 76 //非法输入
  6. 77 return ;
  7. 78 }
  8. 79 //相当于对数组进行尾插
  9. 80 if(heap->size >= HeapMaxSize)
  10. 81 {
  11. 82 //堆满
  12. 83 return ;
  13. 84 }
  14. 85 heap->data[heap->size++] = value;
  15. 86 //对这个堆进行上浮式调整
  16. 87 //调整的起始位置为size-1,即插入的元素
  17. 88 AdjustUp(heap,heap->size-1);
  18. 89 return ;
  19. 90 }

下面为AdjustUp“上浮“函数
首先先要确定父节点与孩子节点的下标,然后若是新插入的节点不能满足堆的定义,就将插入的节点与当前位置的父节点交换位置,直到满足条件为止,
  1. 51 void AdjustUp(Heap* heap,size_t index)
  2. 52 {
  3. 53 //index表示从哪个位置开始调整
  4. 54 size_t child = index;
  5. 55 size_t parent = (child - 1)/2;
  6. 56 while(child > 0)
  7. 57 {
  8. 58 if(!heap->cmp(heap->data[parent],heap->data[child]))
  9. 59 {
  10. 60 Swap(&heap->data[parent],&heap->data[child]);
  11. 61 }
  12. 62 else
  13. 63 {
  14. 64 //若发现某个位置下的child,parent已满足堆的要求,就停止上浮
  15. 65 //因为上面的节点已经满足堆的要求
  16. 66 break;
  17. 67 }
  18. 68 child = parent;
  19. 69 parent = (child - 1)/2;
  20. 70 }
  21. 71 }
  1. 45 void Swap(HeapType* a,HeapType* b)
  2. 46 {
  3. 47 HeapType tmp = *a;
  4. 48 *a = *b;
  5. 49 *b = tmp;
  6. 50 }

4.取堆顶元素
相当于取数组的第一个元素
  1. 92 //取堆顶元素
  2. 93 int HeapRoot(Heap* heap,HeapType* value)
  3. 94 {
  4. 95 if(heap == NULL)
  5. 96 {
  6. 97 //非法输入
  7. 98 return 0;
  8. 99 }
  9. 100 if(heap->size == 0)
  10. 101 {
  11. 102 //空堆
  12. 103 return 0;
  13. 104 }
  14. 105 *value = heap->data[0];
  15. 106 return 1;
  16. 107 }

5.删除堆顶元素
相当于是删除数组的下标为0的元素,但是删除之后还是要求能够满足堆的定义,首先要先将堆顶元素与堆的最后一个元素交换,这样,队首元素就变为了最后一个元素,删除堆顶元素的操作就变成了尾删的操作,但是此时的堆不再满足之前对堆的定义了,要将此时的队首元素进行“下沉”操作来完成,
“下沉”操作:将堆顶元素与其左右子树比较,与较小的子树交换位置,直到满足堆的定义,就停止“下沉”,详细见下图

首先先要对堆进行合法性判断,然后将堆首元素与堆的最后一个元素交换位置,并进行尾删操作,再使用AdjustDown函数将此时的堆首元素进行“下沉”
  1. 136 void HeapErase(Heap* heap)
  2. 137 {
  3. 138 if(heap == NULL)
  4. 139 {
  5. 140 //非法输入
  6. 141 return ;
  7. 142 }
  8. 143 if(heap->size == 0)
  9. 144 {
  10. 145 //空堆
  11. 146 return ;
  12. 147 }
  13. 148 //交换堆顶与最后一个元素
  14. 149 Swap(&heap->data[0],&heap->data[heap->size-1]);
  15. 150 //--size进行尾删
  16. 151 --heap->size;
  17. 152 //从根节点出发进行下沉
  18. 153 AdjustDown(heap,0);
  19. 154 return ;
  20. 155 }
下面为AdjustDown函数将堆首元素进行“下沉”
  1. 109 //删除堆顶元素
  2. 110 void AdjustDown(Heap* heap,size_t index)
  3. 111 {
  4. 112 size_t parent = index;
  5. 113 size_t child = 2*index+1;//左子树
  6. 114 while(child < heap->size)
  7. 115 {
  8. 116 //child+1表示右子树
  9. 117 if(child+1 < heap->size && heap->cmp(heap->data[child+1],heap->data[child]))
  10. 118 {
  11. 119 //若右子树存在,且右子树比左子树更符合堆的要求
  12. 120 //假设为小堆:右子树<做子树,且让child指向右子树
  13. 121 child = child+1;
  14. 122 }
  15. 123 //child指向左右子树中更小的那个元素
  16. 124 if(heap->cmp(heap->data[child],heap->data[parent]))
  17. 125 {
  18. 126 Swap(&heap->data[child],&heap->data[parent]);
  19. 127 }
  20. 128 else
  21. 129 {
  22. 130 break;
  23. 131 }
  24. 132 parent = child;
  25. 133 child = 2*parent+1;
  26. 134 }

6.给一个数组,将数组构建成堆
遍历数组,再将数组中的元素依次插入到堆中即可
  1. 157 //给一个数组,将数组构建成堆,这个堆通过heap来表示
  2. 158 //时间复杂度为O(N*logN)
  3. 159 void HeapCreate(Heap* heap,HeapType arry[],size_t size)
  4. 160 {
  5. 161 if(heap == NULL)
  6. 162 {
  7. 163 return ;
  8. 164 }
  9. 165 //遍历arry数组,将数组元素依次插入到堆中
  10. 166 size_t i = 0;
  11. 167 for(;i<size;++i)
  12. 168 {
  13. 169 HeapInsert(heap,arry[i]);
  14. 170 }
  15. 171 return;
  16. 172 }
  17. 173

7.实现堆排序
堆排序就是首先将数组构建成堆,然后循环将堆进行删除操作,循环结束,堆排序就完成了
注:升序要使用大堆,降序要使用小堆
  1. 174 //实现堆排序
  2. 175 //升序->大堆
  3. 176 //降序->小堆
  4. 177 void HeapSort(HeapType arry[],size_t size)
  5. 178 {
  6. 179 //把数组构建成堆
  7. 180 Heap heap;
  8. 181 HeapInit(&heap,Greater);
  9. 182 HeapCreate(&heap,arry,size);
  10. 183 //循环将堆进行删除操作
  11. 184 while(heap,size>0)
  12. 185 {
  13. 186 HeapErase(&heap);
  14. 187 }
  15. 188 //循环结束后,堆排序完成了
  16. 189 memcpy(arry,heap.data,size* sizeof(HeapType));
  17. 190 return ;
  18. 191 }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/688370
推荐阅读
相关标签
  

闽ICP备14008679号