堆的操作:
- 插入一个数
- 求集合中的最小值
- 删除最小值
- *删除任何一个元素
- *修改任何一个元素
堆的存储
堆是一个完全二叉树。
存储:使用一个一维数组存储,下标从 1 开始存。完全二叉树一般都用一维数组存储。
小根堆:每个点都小于等于左右儿子。
x 的左儿子:2x
x 的右儿子:2x+1
基础操作
down(x)
down(x): 把一个节点往下移动。需要与左右两个孩子比较,与小的孩子交换。时间复杂度与树的高度成正比,因此是 O(log(n))
up(x)
up(x): 把一个节点往上移动。只需与父节点比较,如果比父节点小,就与父节点交换。时间复杂度与树的高度成正比,因此是 O(log(n))
再来看堆的操作
插入一个数
1
| heap[++size] = x; up(size);
|
求集合中的最小值
删除最小值
用堆的最后一个元素覆盖堆顶的元素。将最后一个元素删除。将堆顶元素下移。
1
| heap[1] = heap[size--]; down(1);
|
*删除任何一个元素
1
| heap[k] = heap[size--]; down(k); up(k);
|
down 和 up 只会执行一个,堆最后一个元素要么比第 k 个元素大,要么小。
*修改任何一个元素
1
| heap[k] = x; down(k); up(k);
|
例题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <iostream> #include <algorithm> using namespace std;
const int N = 100010; int n,m; int h[N]; int sz; void 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); } } void up(int u) { while(u/2 && h[u/2]>h[u]) { swap(h[u], h[u/2]); u/=2; } } 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); while(m--) { printf("%d ", h[1]); h[1] = h[sz--]; down(1); } return 0; }
|
难点在于需要维护第 k 个插入的元素对应的堆中的编号关系,还有反查关系,堆中的编号对应的元素是第几次插入的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #include <iostream> #include <algorithm> #include <string.h>
using namespace std;
const int N = 100010; int ph[N], hp[N]; int sz; int h[N]; void heap_swap(int a, int b) { swap(ph[hp[a]], ph[hp[b]]); swap(hp[a], hp[b]); swap(h[a], h[b]); } void 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) { heap_swap(u, t); down(t); } } void up(int u) { while(u/2&&h[u/2]>h[u]) { heap_swap(u/2,u); u/=2; } } int main () { int n, m=0; scanf("%d", &n); while(n--) { char op[10]; int k, x; scanf("%s", op); if(!strcmp(op, "I")) { scanf("%d", &x); sz++; m++; ph[m] = sz; hp[sz] = m; h[sz] = x; up(sz); } else if(!strcmp(op, "PM")) { printf("%d\n", h[1]); } else if(!strcmp(op, "DM")) { heap_swap(1, sz); sz--; down(1); } else if (!strcmp(op, "D")) { scanf("%d", &k); k = ph[k]; heap_swap(k, sz); sz--; down(k); up(k); } else { scanf("%d%d", &k, &x); k = ph[k]; h[k] = x; down(k), up(k); } } return 0; }
|