❤️ 堆实现
💟 1、什么是堆
释义:
堆是计算机科学中一类特殊的数据结构的统称,堆通常是一个可以被看做一颗完全二叉树的数组对象。
堆总是满足下列性质:
- 堆中的某个结点的值总是不大于或不小于其父结点的值
- 堆总是一颗完全二叉树
释义补充:
将根结点最大的堆叫做最大堆或者大根堆,根结点最小的堆叫做最小堆或小根堆
完全二叉树:一颗深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号i的结点在二叉树中的位置相同,则这颗二叉树称为完全二叉树。
如图(以二叉树为3层为例):
满二叉树:二叉树的层数为k,则其总结点数为2^k-1
完全二叉树:二叉树的层数为k,则其总结点数为 2^(k-1) ≤ N ≤2^k -1
最后一层结点数可能不满,但其结点必须从左到右依次排列,不能间隔。所以满二叉树为一种特殊的完全二叉树。
💝 2、堆源码查看
PS:本文主要分析堆实现过程中重点的思想,具体的堆实现代码请移步到代码仓库查看
堆实现源文件查看 ---------------------------------------------->堆实现源文件
堆实现头文件查看 ---------------------------------------------->堆实现头文件
💚 3、堆的基本操作
//初始化堆 void HeapInit(HP* heap); //销毁堆 void HeapDestory(HP* heap); //插入元素 void HeapPush(HP* heap, HeapDataType x); //删除堆顶元素 void HeapPop(HP* heap); //返回堆顶元素 HeapDataType HeapTop(HP* heap); //判断堆是否为空 bool HeapEmpty(HP* heap); //返回堆的size size_t HeapSize(HP* heap); //向下调整函数 void DownAdjusting(HeapDataType* a, size_t size); //向上调整函数 void UpAdjusting(HeapDataType* a, size_t child); //交换元素 void Swap(HeapDataType* child, HeapDataType* parent);
💜 4、向上调整算法、向下调整算法(重点)
🖊(一)向上调整
- 代码查看
void UpAdjusting(HeapDataType* a, size_t child) { size_t parent = (child - 1) / 2; //这里的循环判断条件不能为 parent>=0(在取<时,会多走一次循环条件) 或者 parent>0(最后一次不会进入,即parent为0时) while (child > 0) { #ifdef SmallHeap if (a[child] < a[parent]) { //交换父子节点 Swap(&a[child], &a[parent]); //迭代 child = parent; parent = (child - 1) / 2; } else { break; } #endif //SmallHeap #ifdef BigHeap if (a[child] > a[parent]) { //交换父子节点 Swap(&a[child], &a[parent]); //迭代 child = parent; parent = (child - 1) / 2; } else { break; } #endif // BigHeap } }
- 算法分析
1、向上调整算法用在对堆进行插入数据时。当将新的数据插进堆时,堆原本的结构被破坏了,所以需要向上调整算法进行重新建堆。
如图:
2、算法思想
- 当我们插入数据后的二叉树时,会发现若抹去新插入的结点后,剩余结构为堆的形式。
- 为了满足原来小根堆的结构,即父结点总是小于其子结点的结构,我们需要找到离新插入节点的最近的父节点,然后让其数据与父节点的数据进行比较,若其值比父节点小,则交换,否则就已满足小根堆结构。
交换过程如图:
不断使用向上调整算法后,此时的完全二叉树就又满足堆的性质。
✏️ (二)向下调整
- 代码查看
void DownAdjusting(HeapDataType* a, size_t size) { size_t parent = 0; size_t minchild = parent * 2 + 1; while (minchild < size) { //找出左右孩子最小的一个 if (minchild + 1 < size && a[minchild + 1] < a[minchild]) { minchild++; } //判断大小并交换 #ifdef SmallHeap if (a[minchild] < a[parent]) { //交换 Swap(&a[minchild], &a[parent]); //迭代 parent = minchild; minchild = parent * 2 + 1; } else { break; } #endif #ifdef BIgHeap if (a[minchild] < a[parent]) { //交换 Swap(&a[minchild], &a[parent]); //迭代 parent = minchild; minchild = parent * 2 + 1; } else { break; } #endif // BIgHeap } }
- 算法分析
1、向下调整算法在堆实现过程中用在删除堆顶元素时。
当我们需要删除堆顶元素时,我们是将堆顶元素与最后一个元素进行交换,然后再令size自减后即可删除对堆顶元素
如图:
2、算法思想
- 我们需要对删除堆顶数据后的二叉树进行调整,使他重新具有小堆的结构。
- 此时的二叉树,除去堆顶数据后,剩余的结构仍保持堆的结构,所以只需要对堆顶元素使用向下调整算法即可
- 因为小堆必须保证父结点小于其左右两个结点,所以我们需要对两个子结点进行比较大小,取最小的结点与父节点进行替换。
交换过程如图: