堆属性
堆(Heap)是一类数据结构,它们拥有树状结构,且能够保证父节点比子节点大(或小)。当根节点保存堆中最大值时,称为大根堆;反之,则称为小根堆。
二叉堆(Binary Heap)是最简单、常用的堆,是一棵符合堆的性质的完全二叉树。它可以实现 O(logn) 地插入或删除某个值,并且 O(1)地查询最大(或最小)值。
存储
以小根堆为例
作为一棵完全二叉树,二叉堆完全可以用一个1-index的数组来存储,对于节点p,p2即为左儿子,p2+1即为右节点。同时,用size记录当前二叉堆中节点的个数。
相关操作
up(int x): 和当前节点的根节点比较,如果当前点比根节点更小,则与根节点交换
down(int x): 和当前节点的左右儿子比较,如果左右儿子比当前点小,则把更小的儿子和当前点交换
模板
// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1
// ph[k]存储第k个插入的点在堆中的位置
// hp[k]存储堆中下标是k的点是第几个插入的
int h[N], ph[N], hp[N], size;
// 交换两个点,及其映射关系
void heap_swap(int a, int b)
{
swap(ph[hp[a]],ph[hp[b]]);
swap(hp[a], hp[b]);
swap(h[a], h[b]);
}
void down(int u)
{
int t = u;
if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2;
if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
if (u != t)
{
heap_swap(u, t);
down(t);
}
}
void up(int u)
{
while (u / 2 && h[u] < h[u / 2])
{
heap_swap(u, u / 2);
u >>= 1;
}
}
// O(n)建堆
for (int i = n / 2; i; i -- ) down(i);
题目
代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100005;
int h[N],len;
int n,m;
void down(int u){
int t=u; //t保存最小数的下标
if(2*u<=len && h[2*u]<h[t]){ //下标为u的点有左儿子,左儿子更小
t=2*u; //最小的为左儿子
}
if(2*u+1<=len && h[2*u+1]<h[t]){ //下标为u的右儿子小于当前最小的
t=2*u+1; //最小的为右儿子
}
if(u!=t){ //如果当前值不是最小值,那么更新
swap(h[u],h[t]); //交换最小的到u的位置
down(t); //递归当前(2u+1)下面的值
}
}
void up(int u){
if(u/2>=1 && h[u/2]>h[u]){ //存在父节点,
swap(h[u/2],h[u]);
up(u/2);
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>h[i];
}
len=n;
for(int i=n/2;i>0;i--){ //由于最后一层没有子节点,因此只要循环n/2就行了。
down(i);
}
while(m--){
cout<<h[1]<<" ";
h[1]=h[len]; //取出最小值后,把最后一个值放到第一的位置,总个数减一,然后down
len--;
down(1);
}
return 0;
}