堆
引入
“二叉”自不必多说,本章主要介绍的树都是二叉树。那么啥是“堆”呢? 我们在日常生活中,通常会说“一堆东西”或者“堆东西”,这里的“堆”,通常指重叠放置的许多东西。
我们在堆东西的时候,肯定都有一个经验,即:为了使这堆东西更稳定,会将比较重的、大的东西放在下面,比较轻的、小的东西放在上面。 这个经验放在数据结构——二叉树中,同样适用。只不过“重”“大”是根据结点值的大小来判断的,而且扩展一下最顶端的值可以是最小或最大。
堆(heap)的定义
堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
l 堆中某个结点的值总是不大于或不小于其父结点的值;
l 堆总是一棵完全二叉树。
它是一颗完全二叉树。事实上,该二叉堆就是由完全二叉树经过调整转化而来; 任何一个双亲结点的值,均小于或等于左孩子和右孩子的值; 每条分支从根结点开始都是升序排序(如分支 1-2-3-4)。
这样的二叉堆被称为小根堆,它的堆顶,即根结点 A,是整棵树的最小值。
与小根堆相对应的是大根堆:
大根堆是一颗完全二叉树; 它的任何一个双亲结点的值,均大于或等于左孩子和右孩子的值; 每条分支从根结点开始都是降序排序。 大根堆的堆顶,是整棵树的最大值。
堆的存储
堆的数据结构逻辑上是二叉树,但存放在一维数组里:
堆的根下标为 1;
堆第 i 个元素的左孩子是第 2i 个元素;
堆第 i 个元素的右孩子是第 2i+1 个元素;
向上调整(up,上升,插入)
//读入 n 个整数, 使用向上调整算法 UP(),建立大根堆。然后输出堆数组。
using namespace std;
int n;
int f[2002];
void up(int i){
while(i>1 && f[i/2]<f[i]) {
swap(f[i/2],f[i]);
i = i/2;
}
}
int main(){
cin >> n;
for (int i=1; i<=n; i++){
cin >> f[i];
up(i);
}
for (int i=1; i<=n; i++)
cout << f[i]<<" ";
return 0;
}
向下调整(down,下沉,取堆顶)
// 读入 n 个整数后,使用向下调整算法 Down(),建立小根堆。 先输出堆数组, 然后排序输出。
#include<bits/stdc++.h>
using namespace std;
int n;
int f[2002];
void down(int i){
while (i+i<=n){
int j=i+i;
if (j<n && f[j]>f[j+1])
j++;
if (f[j]>=f[i])
break;
swap(f[j],f[i]);
i=j;
}
}
int main(){
cin >> n;
for (int i=1; i<=n; i++)
cin >> f[i];
for (int i=n/2; i>0; i--) //初始建堆
down(i);
for (int i=1; i<=n; i++)
cout << f[i]<<" ";
cout << endl;
hsort(); //排序输出
return 0;
}
应用
sort: 排序
//接上面的代码
void hsort( ){
while (n>0){
cout << f[1]<<" ";
swap(f[1],f[n]);
n--;
down(1);
}
}
优先队列(取最小值)
? 你能想到那些应用场景? 哈夫曼编码中代码修改
总结
1. 知识点总结
- 堆的定义:堆是一个完全二叉树,可以分为大根堆和小根堆。大根堆的堆顶是最大值,小根堆的堆顶是最小值。
- 存储方式:堆通常使用一维数组来存储,节点按照从上到下、从左到右的顺序依次存入数组中。
- 节点关系:对于节点
i
:- 父节点的下标为
i / 2
- 左子节点的下标为
i * 2
- 右子节点的下标为
i * 2 + 1
- 父节点的下标为
- 大根堆:堆顶元素是整个堆中的最大值。