一、什么是堆
堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
(1)堆中某个节点的值总是不大于或不小于其父节点的值;
(2)堆总是一棵完全二叉树。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。
堆是非线性数据结构,相当于一维数组,有两个直接后继。
堆的定义如下:n个元素的序列{k1,k2,ki,…,kn}当且仅当满足下关系时,称之为堆。
(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4…n/2)
若将和此次序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。由此,若序列{k1,k2,…,kn}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。
————————————————
参考原文链接:https://blog.csdn.net/weixin_44123362/article/details/103873058
二、堆的实现
因为堆是一个二叉树,为了代码的简洁和方便,我们可以用一维数组进行模拟堆。
用数组模拟的二叉树又有性质下标为n的父节点的下标为n/2,根据这个性质我们很快可以将二叉树的左右子树区分开来。如此即可完成堆的简要构建。
三、例题及代码的实现
1.题目:堆模拟
2.题目解析
题目的大致要求为构建一个小根堆并且具备如下功能:
1.插入数据
2.输出最小数据
3.删除最小数据
4.修改或删除任意处的数据
那么为了实现这些功能,首先我们需要构建几个变量用于储存相关数据。
h[N]用来存储数据
ph[N]用来存储第N次插入的数在堆里面的下标
hp[N]用来存储堆里面的下标为N的点是第几次插入的
cnt记录当前数据的个数
m记录插入的次数
其次,我们需要写相关函数用来实现此些功能。
分析功能3,可知实现删除功能不能直接进行删除,而是变相删除,那么可将需要删除的数据与最后一个数据的位置进行交换(此处需要实现交换功能),再将需要删除的数据进行标记处理,别忘了交换后位置的数据还需要进行处理,将堆整理平衡,那么我们又需要实现平衡堆的功能
3.代码的实现
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n,m,h[maxn],cnt,ph[maxn],hp[maxn];
// ph存第k次插入的数在堆里面的下标 hp存堆里面的下标为k的点是第几次插入的
void hswap(int a,int b){
swap(h[a],h[b]);
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a],hp[b]);
}
void down(int u){
int t=u;
if(2*u<=cnt && h[t]>h[2*u])t=2*u;
if(2*u+1<=cnt && h[t]>h[2*u+1])t=2*u+1;
if(t!=u){
hswap(t,u);
down(t);
}
}
void up(int u){
while(u/2 && h[u]<h[u/2]){
hswap(u,u/2);
u/=2;
}
}
int main() {
string str;
cin>>n;
while(n--){
cin>>str;
if(str=="I"){
int x;
cin>>x;
cnt++,m++;
h[cnt]=x;
ph[m]=cnt;
hp[cnt]=m;
down(cnt),up(cnt);
}
else if(str=="PM"){
cout<<h[1]<<endl;
}
else if(str=="DM"){
hswap(1,cnt);
cnt--;
down(1),up(1);
}
else if(str=="D"){
int k;
cin>>k;
k=ph[k];//此处将k修改为第k次插入的数在堆中的下标
hswap(k,cnt);
cnt--;
down(k),up(k);
}
else if(str=="C"){
int k,x;
cin>>k>>x;
k=ph[k];
h[k]=x;
down(k),up(k);
}
}
return 0;
}