文章目录
- 一.简介
- 二.实现
- 三.具体代码(大根堆)
- 1.初始化
- 2.调整
- 3.插入
- 4.删除
- 5.取最值
一.简介
二叉堆支持插入、删除、查询最值,和stl库中的优先队列类似。相比优先队列,二叉堆还可以支持删除特定结点的操作,但是写法相比直接调用stl库的优先队列繁琐很多,因此应用不多。
不过本着学习的心态,还是有必要一看的:
二叉堆是一个满足“大/小根堆”性质的完全二叉树。
大根堆的任意子结点的权值都小于父结点权值
小根堆的任意子结点的权值都大于父结点权值
记忆方法很简单,小根堆顾名思义根结点权值比较小,大根堆顾名思义根结点权值比较大
二.实现
可以用一个数组来实现二叉堆。采取层序遍历从左到右的顺序给结点编号:
根据完全二叉树的性质,若一个结点的编号为p,则其父结点为p/2(这里的除是整除),其子节点为p×2和p×2+1(如果存在结点的话)
插入删除操作都在数组末尾进行:
插入操作:先将元素加入数组末尾,然后依据二叉堆的性质进行调整,时间复杂度
删除操作:先将元素与数组末尾交换,删除数组末尾,然后依据二叉堆的性质对元素进行调整
三.具体代码(大根堆)
用一个变量cnt分配结点
用数组heap储存二叉堆
int heap[maxn];
int cnt=1;
1.初始化
void init(){
memset(heap,0,sizeof(heap));
cnt=1;
}
2.调整
向上调整函数
void up(int p){
while(p>1){
if(heap[p]<=heap[p/2])// 满足大根堆,不需要调整
break;
swap(heap[p],heap[p/2]);
p/=2;
}
}
向下调整函数
void down(int p){
int s=p*2;
while(s<cnt){
if(s<cnt-1&&heap[s]<heap[s+1]) //取子结点中较大的
s++;
if(heap[s]<=heap[p]) break;
swap(heap[p],heap[s]);
p=s;s=p*2;
}
}
3.插入
void Insert(int x){
heap[cnt]=x;
up(cnt);
cnt++;
}
插入一个结点,先将其插入到数组的末尾(即二叉树的末尾),然后向上调整即可
4.删除
void Remove(int k){
heap[k]=heap[--cnt];
up(k); down(k);
}
删除结点,先将其与数组末尾交换,然后再调整。
删除堆顶是删除的特殊情况,因为在堆顶不需要向下调整:
void Extract(){
heap[1]=heap[--cnt];
down(1);
}
5.取最值
二叉堆堆顶heap[1] 存放的就是最值