学习笔记:堆
前言
堆是在大纲里面的 但是大家都在有 priority_queue
优先队列 以至于很少人去学习手写堆
因此 需要补一补
概念
什么是堆?
就是一棵完全二叉树 且任意当前节点必定比儿子大/小 这就是大/小根堆
性质:堆的子树也是堆
步骤
1.查找最小值
很简单 返回堆顶即可
2.插入一个数
为了不破坏完全二叉树的性质 选择在叶子节点加一个数 这样会破坏堆的性质
怎么办?
维护以下就行
暴力把这个点往上旋转 知道满足堆性质就行了!
void Pushup(int x)
{
while(x>1)
{
if(val[x]<val[x/2])swap(val[x],val[x/2]);
else break;
x/=2;
}
return ;
}
void insert(int x)
{
val[++tot]=x;
Pushup(tot);
}
很简单
3.删除堆顶
这个难搞 可不想把一棵树分裂成两棵
不妨这样想 把根节点丢到堆底 删掉堆底就行了
但这样子会使得根破坏性质
怎么办?
考虑下转
考虑维护小根堆
若当前节点比两个儿子节点大 那就把当前节点和小的那个点转一下就行
转到满足即可
注意判断边界
void insert(int x)
{
val[++tot]=x;
Pushup(tot);
}
void Pushdown(int x)
{
while(x*2<=tot)
{
int son=x*2;
if(son+1<=tot&&val[son+1]<=val[son]) son++;
if(val[x]>val[son])swap(val[son],val[x]);
else break;
x=son;
}
}
所有操作都结束了 十分简单!
现在分析时间复杂度
首先 因为保证是 完全二叉树 因此树高不超过\(O(\log n)\)
因此 查询是 \(O(1)\) 的
删除,增加复杂度是 \(O(\log n)\) 的
因此 总时间复杂度为 \(O(n\log n)\)