5、二叉树-小堆
🧱 什么是小堆?
小堆是一种完全二叉树,满足以下性质:
- 每个节点的值都小于或等于它的左右子节点。
- 最小值始终位于堆顶(根节点)。
🛠️ 构建小堆的步骤
假设我们有一个数组:
int a[] = {30, 20, 10, 25, 15, 40};
我们要将它构建成一个小堆。
🔍 Step 1:从最后一个非叶子节点开始调整
非叶子节点的最后一个下标是 (n - 2) / 2
,这里是 (6 - 2) / 2 = 2
,即下标为 2 的元素是 10
。
我们从下标 2 开始,向下调整每个节点,使其满足小堆性质。
🔄 Step 2:向下调整(AdjustDown)
✅ 调整下标为 2 的节点(值为 10)
- 左子节点:下标 5,值为 40
- 没有右子节点
- 10 已经小于 40,无需调整
✅ 调整下标为 1 的节点(值为 20)
- 左子节点:下标 3,值为 25
- 右子节点:下标 4,值为 15
- 右子节点更小 → 交换 20 和 15
数组变为:{30, 15, 10, 25, 20, 40}
继续向下调整下标 4(值为 20),它是叶子节点 → 停止
✅ 调整下标为 0 的节点(值为 30)
- 左子节点:下标 1,值为 15
- 右子节点:下标 2,值为 10
- 右子节点更小 → 交换 30 和 10
数组变为:{10, 15, 30, 25, 20, 40}
继续向下调整下标 2(值为 30):
- 左子节点:下标 5,值为 40
- 没有右子节点
- 30 已经小于 40 → 停止
✅ 最终小堆结构
数组:{10, 15, 30, 25, 20, 40}
对应的堆结构如下:
10/ \15 30/ \ \25 20 40
每个父节点都小于或等于它的子节点,满足小堆性质。
代码实现
// 交换数据
void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}
// 先把外部给我的数组变成堆,向下调整,前提是左右子树都是小堆
void AdjustDown(HPDataType* a, int n, int root)
{int parent = root;int child = 2*parent + 1;// 这里就是左孩子// 然后找出左孩子和又孩子之中最小的那个while(child < n){// child+1有可能越界if(child+1 < n && a[child + 1] < a[child]){// 此时最小的是child+1++child;}// 如果a[parent]小于a[child]if(a[child] < a[parent]){// 完成了数据的交换Swap(&a[parent], &a[child]);parent = child;child = 2*parent + 1;}else{break;}}}
// 初始化
void HeapInit(HP* php, HPDataType* a, int n)
{php->_a = (HPDataType*)malloc(sizeof(HPDataType)*n);memcpy(php->_a, a, sizeof(HPDataType)*n);php->_size = n;php->_capacity = n;// 构建堆,怎么把左右子树变成小堆// 从倒数第二个非叶子节点去调整for(int i = (n-1-1) / 2;i >= 0; --i){// 慢慢调整使得满足条件,左右子树都是小堆或者大堆AdjustDown(php->_a, php->_size, i);}}