\qquad
堆是一种树形数据结构,对于一个堆,其一定是一个完全二叉树而小根堆则额外满足左右子节点都大于父节点,大根堆则满足左右子节点都小于父节点,一般应用于求出某组数中的最大值或最小值(即根节点),本文主要介绍二叉堆,其他堆我都不会
堆的性质
∙
\qquad\bullet
∙堆一定是一个完全二叉树(根据其定义)
∙
\qquad\bullet
∙堆中最大(最小)值即为根节点
∙
\qquad\bullet
∙每个堆的子树依然是一个堆
堆的操作
\quad 1.插入
假设你已经有了一个堆,如下
现在要将一个新节点0插入堆中
首先建立新节点储存0,然后将其与父节点比较,如果小于父节点则将其与父节点交换位置
哦吼,好像不太行
还是不行
舒服了
直到其变为根节点或者父节点小于它
void push(int k){
heap[++ top] = k;
int now = top;
int fa = now >> 1;
while((heap[fa] > heap[now]) && (now > 1)){
swap(heap[fa],heap[now]);
now = fa;
fa = now >> 1;
}
}
\quad 2.删除
插入完0后又觉得很不爽(一群正数里面混了个0 )又想把他删了,那怎么办呢?
先将其与叶节点交换,然后
t
o
p
−
−
top --
top−−即可将其删除,再将交换后的节点与左右子节点比较,大于则交换下沉,重复此操作来维护堆的性质
完成啦
void del(){
swap(heap[1],heap[top]);
top --;
int now = 1;
while((now << 1) <= top){
int son = now << 1;
if((heap[son + 1] < heap[son]) && (son + 1 <= top)) son ++;
if(heap[son] < heap[now]) swap(heap[son],heap[now]);
else break;
now = s;
}
}
\quad 3.查询
直接输出堆顶就好
堆的STL实现
大人,时代变了
应该没人手写堆了吧 情怀党?
用优先队列实现堆,代码上完爆手写,速度上手写快一点,但是开了
O
2
O2
O2优化优先队列可能会快一点
#include<queue> //优先队列头文件
priority_queue<int> q1; //大根堆
priority_queue<int,vector<int>,greater<int> >q2; //小根堆
q.top(); //取堆顶元素
q.pop(); //弹出堆顶元素
q.push(); //将某元素插入堆中
q.empty(); //判断堆是否为空,空返回1,否则返回0
q.size(); //查询堆内元素数量
在STL中使用优先队列经常会有到重载运算符
我会在另一篇博客中详解重载运算符
堆的复杂度
插入删除复杂度位 O ( log 2 n ) O(\log_2n) O(log2n)
堆的应用
1.堆排序
正常建堆,一次弹出即可
但是裸的堆排序并没有什么用,
s
o
r
t
sort
sort比堆排序快而且写起来方便,大部分时候用到堆排序都是一些奇奇怪怪的操作
2.用2个堆维护查询第k小的操作
一个大根堆一个小根堆来维护
k
k
k小,并没有强制在线
用一个大根堆来存储
k
−
1
k-1
k−1个元素,当其元素为
k
k
k个时,弹出堆顶并将堆顶插入小根堆中,查询结果就是小根堆堆顶,以为
k
−
1
k-1
k−1小全在大根堆中