浅谈堆
堆是一种支持插入,查询最值以及删除最值的数据结构。
堆是一棵二叉树,这棵二叉树所有的叶子都集中在最下面两层,并且如果一个点有右儿子的话一定有左儿子,而且最下面的叶子全部都靠在左边。由于这个性质,我们可以很快找到一个点\(p\)的父亲\(p>>1\)和左右儿子\(p<<1\)以及\(p<<1|1\)。
堆满足任意一个结点的权值都小于/大于父亲结点的权值的性质,我们称这个性质为堆性质。那么显然,堆顶就是最值所在。
插入
在最下层新建一个叶子结点,权值为插入的数值。为了满足堆性质,不断上浮去维护,直到这个点满足堆性质了才停下来。以小根堆为例:
代码如下:
void ins(int x) {
tree[++tot]=x;
int pos=tot;
while(pos>1) {
if(tree[pos]<tree[pos>>1])
swap(tree[pos],tree[pos>>1]),pos>>=1;
else break;
}
}
删除并返回最值
把堆顶返回,然后把最底部的值放到堆顶,不断下沉去维护堆性质。下沉的时候注意左右儿子拥有最值的那个交换上来。以小根堆为例:
代码如下:
int pop() {
int res=tree[1];
tree[1]=tree[tot--];
int pos=1,son=2;
while(son<=tot) {
if(son<tot&&tree[son|1]<tree[son])son|=1;
if(tree[son]<tree[pos])
swap(tree[son],tree[pos]),pos=son,son=pos<<1;
else break;
}
return res;
}