笔记:
堆是二叉树的结构,我们要利用堆实现以下五个操作:
1. 插入一个数
2. 求集合最值
3. 删除最值
4. 删除任意元素
5. 修改任意元素
前三个操作可以采用STL直接实现,然而后两个就不行了。
堆删除最后一层之后是一个完全二叉树,我们以小根堆为例。
如何存储?
我们以下标作为编号,编号为1的是根节点,随后用父子2倍表示法,也就是x的左儿子是2x,右儿子是2x+1。
小根堆的堆顶是Min。
我们来看一下heap的两种基本操作up和down。
- down(x) 往下移:x本身与x的两个儿子中更小的交换,直到无法交换。
- up(x) 往上移:x本身与x的父节点更小的交换,直到无法交换。
接下来是操作的实现。
我们设heap数组就是编号数组。
insert(x):
heap[++size] = x;
up(size)
query
return heap[1];
delete
heap[1] = heap[size];
size--;
down(1);
del(k)
heap[k] = heap[size];
size--;
down(k); up(k); //干脆up和down一起执行,因为毕竟这只会选择其中一个执行。
change(k,x)
heap[k] = x;
down(k);
up(k);
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m, h[N], sz = 0;
int down(int u) {
int t = u;
if (u * 2 <= sz && h[u * 2] < h[t]) t = u * 2;
if (u * 2 + 1 <= sz && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
if (u != t) {
swap(h[u], h[t]);
down(t);
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &h[i]);
sz = n;
for (int i = n / 2; i ; i--) down(i); //O(n)建堆
while (m--) {
printf("%d ", h[1]);
h[1] = h[sz]; sz--;
down(1);
}
return 0;
}
这个sz 一开始不用赋值为0,然后这个down 函数用void 吧??(不知道我说的对不对,我是小白一个)
是的,你别说,2022 年写这篇题解的时候我也是小白 :<
像我写 down 函数用 int 返回的话可能会有未定义行为,导致 RE 之类的错误
😁😁
为什么没有up()函数,没有什么作用吗
因为这个本质上已经建好堆了,只是一直在删除(delete)最小的元素,而删除不用 up
谢谢你
谢谢
已修改
???大佬又在搞什么神奇的我不会得算法