赞
踩
目录
此处可能会有疑问,左右孩子的父节点计算为什么可以归纳为一个结论了?
普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
如果有一个关键码的集合k ={ },把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:
且
且
i = 0,
2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
要满足且
且
的原因。
如果要用数组存储二叉树,那么必须要符合顺序存储中父子存储的规律
但是数组存储二叉树是有要求的。如果不符合该规律,那么得设置空节点去代替缺失的节点(因为要满足下标的规律才能方便查找),那么使用太多的空节点会造成空间的浪费。
结论:数组存储只适合完全二叉树和满二叉树
堆并非是一定有序的 :左孩子与右孩子之间没有大小关系
这种特性使得堆成为一种非常有效的数据结构,特别是在实现优先队列等应用中。堆可以在对数时间内完成插入和删除最大(或最小)元素的操作,这是因为它不需要保持整个结构的完全排序。
举个例子:
- 10
- / \
- 5 8
- / \ / \
- 2 3 6 7
在这个堆中,父节点的值总是大于或等于其子节点的值。但是,你可以看到左孩子和右孩子之间的大小关系是不一致的。例如,5的左孩子是2,右孩子是3,而8的左孩子是6,右孩子是7。这里并没有规定左孩子必须大于或小于右孩子。
- void Swap(HPDataType* px,HPDataType* py)
- {
- *py ^= *px;
- *px ^= *py;
- *py ^= *px;
- }
目的:
当向堆中插入新元素时,为了维护堆的性质,需要对该元素进行向上调整。向上调整法就是从新插入的节点开始,通过与其父节点的比较和交换,确保该节点的值不大于(对于大根堆)或不小于(对于小根堆)其父节点的值。
步骤:
- 插入数据
- 与自己的父亲比较
- 交换/不交换
- 交换:孩子来到父亲位置,父亲来到自己父亲的位置。
判断条件:a[child] > a[parent]
结束循环条件:child > 0 (确保不是根节点)
时间复杂度:O(logN),其中N是堆中元素的数量。因为每次调整都涉及沿着树的一条路径向上移动,而树的深度为logN。
void AdjustUP(HPDataType* a, int n, int parent)参数的意义:
- void AdjustUp(HPDataType* a, int child)
- {
- int parent = (child - 1) / 2;// 获取父节点索引
- //while (parent >= 0)
- while(child > 0)// 确保不是根节点
- {
- //if (a[child] < a[parent])// 孩子小于于父亲,需要交换,向下调整法
- if (a[child] > a[parent])// 孩子大于父亲,需要交换, 向上调整法
- // 如果孩子节点大于父节点,则交换
- {
- Swap(&a[child], &a[parent]);
- child = parent;// 移动到父节点
- parent = (parent - 1) / 2;
- }
- else {
- break;
- }
- }
-
- }

可得高度与向上调整的关系
时间复杂度
目的:
当从堆中移除元素(通常是堆顶元素)后,为了维护堆的性质,需要对剩余的元素进行重新调整。向下调整法就是从父节点开始,通过与其子节点的比较和交换,确保父节点的值不大于(对于大根堆)或不小于(对于小根堆)其子节点的值。
步骤:
- 删除堆顶元素
- 堆顶元素与最后一个元素交换
- 删除最后一个元素
- 堆顶元素与左右两个孩子(最小/最大的孩子比较)
- 判断交换/不交换
- 交换:父亲来到孩子位置,孩子来到自己孩子的位置
判断条件:child + 1 < n && a[child + 1] < a[child]
结束循环条件:child < n(确保左孩子存在)
时间复杂度:O(logN),其中N是堆中元素的数量。因为每次调整都涉及沿着树的一条路径向下移动,而树的深度为logN。
如何删除堆顶数据后插入数据?
向下调整法
如果直接挪动覆盖:操作的时间复杂度太大,关系太乱,不如重新建堆
向下调整法:
void AdjustDown(HPDataType* a, int n, int parent)参数的意义:
- // 向下调整算法(用于删除或构建堆时维护堆)
- void AdjustDown(HPDataType* a, int n, int parent)
- {
- int child = parent * 2 + 1; // 获取左孩子索引
- while (child < n) // 确保左孩子存在
- {
- // 如果右孩子存在且大于左孩子,则选择右孩子
- if (child + 1 < n && a[child + 1] > a[child])
- {
- ++child; // 选择右孩子
- }
-
- // 如果孩子节点大于父节点,则交换
- if (a[child] > a[parent])
- {
- Swap(&a[child], &a[parent]);
- parent = child; // 移动到孩子节点
- child = parent * 2 + 1; // 获取新的左孩子索引
- }
- else {
- break; // 不需要交换,退出循环
- }
- }
- }

可得高度与向下调整次数的关系
可得时间复杂度:
有一个数列,请用堆排序升序排列
如果使用向下调整法建小堆,先把0视为堆根,0和3交换,然后当3视为堆根时:
所以要建大堆:
堆排序的时间复杂度与向上调整法建堆时差不多
子节点大于父节点时交换,建大堆,升序,保证父节点小于子节点
子节点小于父节点时交换,建小堆,降序,保证父节点大于子节点
- #include<bits/stdc++.h>
- using namespace std;
-
- void Swap(int* px, int* py)
- {
-
- *py ^= *px;
- *px ^= *py;
- *py ^= *px;
- }
- // 向下调整算法(用于删除或构建堆时维护堆)
- void AdjustDown(int* a, int n, int parent)
- {
- int child = parent * 2 + 1; // 获取左孩子索引
- while (child < n) // 确保左孩子存在
- {
- // 如果右孩子存在且大于左孩子,则选择右孩子
- if (child + 1 < n && a[child + 1] < a[child])
- {
- ++child; // 选择右孩子
- }
-
- // 如果孩子节点大于父节点,则交换
- if (a[child] < a[parent])
- {
- Swap(&a[child], &a[parent]);
- parent = child; // 移动到孩子节点
- child = parent * 2 + 1; // 获取新的左孩子索引
- }
- else {
- break; // 不需要交换,退出循环
- }
- }
- }

- void HeapSort(int* a, int n)
- {
- // a数组直接建堆 O(N)
- for (int i = (n - 1 - 1) / 2; i >= 0; --i)
- {
- AdjustDown(a, n, i);
- }
-
- // O(N*logN)
- int end = n - 1;
- while (end > 0)
- {
- Swap(&a[0], &a[end]);// 首尾交换
- AdjustDown(a, end, 0);// 向下调整
- --end;
- }
- }

这个函数首先通过AdjustDown函数将数组转化为最大堆。然后,它反复地将堆的根节点(即最大元素)与堆的最后一个节点交换,并重新调整堆,直到整个数组被排序。
- int main()
- {
- int a[] = { 3,6,1,5,8,9,2,7,4,0 };
-
- HeapSort(a, sizeof(a) / sizeof(int));
- for (int i = 0; i < 10; i++)
- printf("%d", a[i]);
-
- return 0;
- }
今天就先到这了!!!
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。