堆,又叫优先队列。堆是一颗完全二叉树结构(除了最后一层节点之外,上面所有节点都是为满的,最后一层,从左到右排列)。
手写堆的基本要求:
- 插入一个数
- 求集合中的最小值
- 删除最小值
- 删除任意一个元素
- 修改任意一个元素
小根堆:
大根堆:
堆的两个基本操作:
down:
把一个节点往下移动,使满足根堆的性质
up
把一个节点往上移动,使满足根堆的性质
- 如果根节点比当前节点大,就交换
- 交换到不能交换或者到顶根节点
堆节点的组合操作
插入操作:
删除最小值操作:
- heap[1] = heap[size], size–; down(1);
删除任意一个元素:
- heap[k] = heap[size]; size–; up[k];
修改任意一个元素:
- heap[k] = x; down(k); up(k);
注意: 下标从1开始比较方便。
题目:堆排序
来源: https://www.acwing.com/problem/content/840/
输入一个长度为n的整数数列,从小到大输出前m小的数。
输入格式
第一行包含整数n和m。
第二行包含n个整数,表示整数数列。
输出格式
共一行,包含m个整数,表示整数数列中前m小的数。
数据范围
$1≤m≤n≤10^5,$
$1≤数列中元素≤10^9$
输入样例:
输出样例:
| 12
 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
 
 | #include <iostream>
 using namespace std;
 const int N = 100010;
 
 int n, m;
 int h[N], cnt;
 
 void down(int x) {
 int t = x;
 if (2 * x <= cnt && h[2 * x] < h[t]) t = 2 * x;
 if (2 * x + 1 <= cnt && h[2 * x + 1] < h[t]) t = 2 * x + 1;
 if (t != x) {
 swap(h[t], h[x]);
 down(t);
 }
 }
 
 
 int main() {
 cin >> n >> m;
 for (int i = 1; i <= n; i++) scanf("%d", &h[i]);
 
 cnt = n;
 for(int i = cnt / 2; i; i--) down(i);
 
 for(int i = 0; i < m; i++) {
 printf("%d ", h[1]);
 h[1] = h[cnt--];
 down(1);
 }
 return 0;
 }
 
 | 
题目: 模拟堆
来源: https://www.acwing.com/problem/content/841/
维护一个集合,初始时集合为空,支持如下几种操作:
“I x”,插入一个数x;
“PM”,输出当前集合中的最小值;
“DM”,删除当前集合中的最小值(数据保证此时的最小值唯一);
“D k”,删除第k个插入的数;
“C k x”,修改第k个插入的数,将其变为x;
现在要进行N次操作,对于所有第2个操作,输出当前集合的最小值。
输入格式
第一行包含整数N。
接下来N行,每行包含一个操作指令,操作指令为”I x”,”PM”,”DM”,”D k”或”C k x”中的一种。
输出格式
对于每个输出指令“PM”,输出一个结果,表示当前集合中的最小值。
每个结果占一行。
数据范围
$1≤N≤10^5$
$−10^9≤x≤10^9$
数据保证合法。
输入样例:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | 8I -10
 PM
 I -10
 D 1
 C 2 8
 I 6
 PM
 DM
 
 | 
输出样例:
| 12
 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
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 
 | #include <iostream>#include <algorithm>
 #include <string.h>
 using namespace std;
 
 const int N = 100010;
 
 
 
 int h[N], ph[N], hp[N], cnt;
 
 void heap_swap(int x, int y)
 {
 
 
 
 
 swap(ph[hp[x]], ph[hp[y]]);
 swap(hp[x], hp[y]);
 swap(h[x], h[y]);
 }
 
 void down(int x)
 {
 int t = x;
 if ( 2 * x <= cnt && h[2 * x] < h[t]) t = 2 * x;
 if ( 2 * x + 1 <= cnt &&  h[2 * x + 1] < h[t]) t = 2 * x + 1;
 if (t != x)
 {
 heap_swap(t, x);
 down(t);
 }
 }
 
 void up(int x) {
 while(x / 2 && h[x / 2] > h[x])
 {
 heap_swap(x / 2, x);
 x >>= 1;
 }
 }
 
 
 int main()
 {
 int n, m = 0;
 scanf("%d", &n);
 while (n -- )
 {
 char op[5];
 
 int k, x;
 scanf("%s", op);
 
 
 if (!strcmp(op, "I"))
 {
 scanf("%d", &x);
 cnt ++ ;
 m ++ ;
 ph[m] = cnt, hp[cnt] = m;
 h[cnt] = x;
 up(cnt);
 }
 else if (!strcmp(op, "PM")) printf("%d\n", h[1]);
 else if (!strcmp(op, "DM"))
 {
 heap_swap(1, cnt);
 cnt -- ;
 down(1);
 }
 else if (!strcmp(op, "D"))
 {
 scanf("%d", &k);
 k = ph[k];
 heap_swap(k, cnt);
 cnt -- ;
 up(k);
 down(k);
 }
 else
 {
 scanf("%d%d", &k, &x);
 k = ph[k];
 h[k] = x;
 up(k);
 down(k);
 }
 }
 }
 
 |