数据结构之堆(Heap)详解
创作时间:
作者:
@小白创作中心
数据结构之堆(Heap)详解
引用
CSDN
1.
https://blog.csdn.net/qq_61820853/article/details/138626255
堆(Heap)是一种特殊的数据结构,广泛应用于计算机科学领域。本文将详细介绍堆的概念、实现方法及其在堆排序和Top-K问题中的应用。
一、堆的概念及结构
堆(Heap)是一种特殊的非线性结构,其元素按照完全二叉树的顺序存储方式存储在数组中。堆可以分为大根堆和小根堆:
- 大根堆:所有父结点都大于等于左右孩子结点。
- 小根堆:所有父结点都小于等于左右孩子结点。
堆具有以下性质:
- 堆是一棵完全二叉树。
- 堆中某个节点的值总是不大于或不小于其父节点的值。
- 如果一个堆为大(小)根堆,则它的左右子树也都是大(小)根堆。
- 小堆的存储结构不是升序,大堆的存储结构也不是降序。
- 堆中任意结点下标为 i,则它的左孩子结点下标为2i+1,右孩子结点下标为2i+2,父结点下标为(i-1)/2。
二、堆的实现
堆的实现主要依赖两个重要算法:向上调整和向下调整。
1. 向上调整算法
向上调整算法通常在堆中插入元素时使用。具体实现如下:
- 给定一个数组和一个结点下标,该结点与其父结点的值进行比较。
- 如果该结点的值 ≥ 其父结点的值,已经是小堆了,则不再继续调整;
- 如果该结点的值 < 其父结点的值,需要将二者交换,然后将父结点当做孩子结点,继续向上对比和交换,直到调整到堆顶。
//小根堆 向上调整
void AdjustUp(DataType* a, int child)
{
int parent = (child - 1) / 2;//父结点下标
while (a[child] < a[parent])//建大堆
{
Swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
}
//交换函数
void Swap(DataType* p1, DataType* p2)
{
DataType tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
向上调整算法的时间复杂度为O(logN)。
2. 向下调整算法
向下调整算法在堆排序和堆的创建中经常使用。其前提条件是左右子树必须是一个堆。
具体实现如下:
- 从该结点开始,与其左右孩子结点中较大(较小)的结点进行比较。
- 如果该结点的值 ≤ 其较小(较大)孩子结点的值,已经是小堆了,则不再继续调整;
- 如果该结点的值 > 其较小(较大)孩子结点的值,需要将二者交换,然后将较小的孩子结点当做父结点,继续向下对比和交换,直到调整到叶子结点。
//向下调整
void AdjustDown(DataType* a, int parent, int n)
{
int child = 2 * parent + 1;
while (child < n)
{
//选出较小的孩子
if (child + 1 < n && a[child + 1] < a[child])//右孩子不能越界访问,注意判断顺序不能反
{
child++;
}
if (a[parent] > a[child])
{
Swap(&a[parent], &a[child]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
向下调整算法的时间复杂度也为O(logN)。
3. 堆的创建
向上调整建堆
从根结点的左孩子结点开始,每个结点都向上调整,直到调整到最后一个结点,就可以调整成堆。时间复杂度为O(N*logN)。
向下调整建堆
从最后一个非叶子结点开始倒着调整,时间复杂度为O(N)。实际应用中,一般都使用向下调整算法建堆。
//向下调整建堆 效率O(N)
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(a, i, n);
}
4. 堆的插入
堆的插入需要用到向上调整算法。
- 在堆的末尾插入一个元素;
- 该元素向上调整,直到满足堆的性质。
//入堆
void HeapPush(Heap* php, HDataType x)
{
assert(php);
//扩容
if (php->size == php->capacity)
{
HDataType* tmp = (HDataType*)realloc(php->a, sizeof(HDataType) * (INC_SZ + php->capacity));
if (NULL == tmp)
{
perror("malloc");
return;
}
php->a = tmp;
php->capacity += INC_SZ;
}
php->a[php->size++] = x;//尾插
AdjustUp(php->a, php->size - 1);//向上调整
}
5. 堆的删除
删除的是堆顶的元素,删除之后仍然保证是堆。
- 将堆顶元素与最后一个元素交换;
- 堆长度-1,即删除最后一个位置;
- 将交换后的堆顶元素向下调整。
//出堆
void HeapPop(Heap* php)
{
assert(php);
assert(!HeapEmpty(php));
//将尾数据和堆顶数据交换,交换后的堆顶元素再向下调整
Swap(&php->a[php->size - 1], &php->a[0]);
php->size--;//堆的有效长度-1
AdjustDown(php->a, 0, php->size);
}
6. 堆的其他操作
//返回堆顶元素
HDataType HeapTop(Heap* php)
{
assert(php);
return php->a[0];
}
//判堆空
bool HeapEmpty(Heap* php)
{
assert(php);
return php->size == 0;
}
//堆的元素个数
int HeapSize(Heap* php)
{
assert(php);
return php->size;
}
三、堆的应用
1. 堆排序
堆排序的实现步骤:
- 先将数组建堆;
- 将堆顶元素与堆末尾元素交换;
- 堆有效长度-1;
- 再将堆顶元素向下调整,直到调整到成堆
堆排序的时间复杂度是O(N*logN)。
//堆排序
void HeapSort(int* a, int n)
{
//建堆:排升序建大堆,排降序建小堆
//倒着调整,从最后一个非叶子结点开始向下调整建堆 效率O(N)
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(a, i, n);
}
//O(N*logN)
int end = n - 1;//堆的有效长度
while (end > 0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, 0, end);
end--;
}
}
2. Top-K问题
Top-K问题:求集合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
实现方法:
- 用数据集合中前K个元素来建堆。
- 前k个最大的元素,则建小堆
- 前k个最小的元素,则建大堆
- 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素。比较完后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。
void TopK(int* a, int n, int k)
{
//用a中前k个元素建堆
for (int i = (k - 2) / 2; i >= 0; i--)
AdjustDown(a, i, k);
for (int i = k; i < n; i++)
{
if (a[i] < a[0])
{
Swap(&a[i], &a[0]);//不满足则交换
AdjustDown(a, 0, k);//向下调整
}
}
for (int i = 0; i < k; i++)
printf("%d ", a[i]);
}
堆的完整代码可以参考:https://gitee.com/ncu-ball/study/tree/master/24_4_19
热门推荐
2025年冬天防寒"三明治"穿搭火了!时尚达人示范,保暖时髦
浅析《赤壁赋》思想内容及艺术特点
文言文《赤壁赋》的结构分析
商务部:今年前8个月我国与北欧五国贸易额达354.4亿美元
C++语言中如何使用绝对值
泰戈尔的作品:诗歌、散文与思想的交响
几何方法求解n个自然数的平方和
“贡”享其美 青海黄南精彩呈现热贡文化艺术盛宴
传国玉玺遗失后的皇位传承:秦始皇后的历史篇章
中国玺印史上独一无二的独孤信印
如何通过U盘进入PE模式进行系统修复与数据恢复的详细指南
梦见野猫进家的寓意解析
手机屏幕碎了该如何处理?修理、备份与换机全攻略!
如何获取汽车保险的报价?这种报价有什么参考价值?
万能炒菜秘籍,让厨艺瞬间飙升!
火绳枪的历史:诞生、辉煌、没落
【智能优化算法】 —— 一文搞懂模拟退火
资源丰富、避开两次世界大战的南美,为何到现在都没有发达国家?
肾衰竭患者透析治疗指南:从原理到护理全方位详解
治疗肾功能衰竭的药物有哪些
新版v2ray如何设置全局代理?新手小白也能会
电磁炉3500w好还是2200瓦好?电磁炉功率越大越好吗?
塞维利亚旅游指南:在塞维利亚做什么和住在哪里
农民隐士无量子,湘西巫傩文化传承人
房贷刷新!全年多项房贷利率调整批量兑现,你的月供一共降了多少?
数据结构详解:B+树、B*树及MySQL底层引擎应用
王者荣耀术语全部解释(王者荣耀术语大全)
2024年十大催泪游戏推荐:让游戏触动你的心灵
二次函数的顶点坐标公式是什么 如何应用
保护口腔横向刷牙的危害与巴氏刷牙法科普动画