【模板】堆
原题目:
luogu-P3378
简要描述:
这是一个非常经典的利用 堆 (Heap) 数据结构的问题。在 C++ 中,STL 提供了 std::priority_queue(优先队列),它的底层实现通常就是堆。
以下是使用 C++ priority_queue 解决该问题的完整代码及详细解析。
使用STL 代码
cpp
copy
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
#include <iostream>
#include <queue>
#include <vector>
#include <functional> // 包含 greater<int>
using namespace std;
int main() {
// 1. IO 提速
// 数据规模 n <= 10^6,输入输出量很大,必须解除 C++ 标准流与 C 标准流的绑定
ios::sync_with_stdio(false);
cin.tie(NULL);
// 2. 定义小根堆
// 默认 priority_queue 是大根堆(大的在堆顶)
// 加上 vector<int> 和 greater<int> 参数将其变为小根堆(小的在堆顶)
priority_queue<int, vector<int>, greater<int>> pq;
int n;
if (!(cin >> n)) return 0; // 读取操作次数
while (n--) {
int op;
cin >> op;
if (op == 1) {
int x;
cin >> x;
pq.push(x); // 操作1: 加入元素 O(log N)
}
else if (op == 2) {
// 操作2: 输出最小数
// 小根堆的堆顶 pq.top() 即为最小值 O(1)
cout << pq.top() << "\n";
}
else if (op == 3) {
// 操作3: 删除最小数
// 删除堆顶元素 O(log N)
pq.pop();
}
}
return 0;
}
手写堆
cpp
copy
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <iostream>
#include <vector>
#include <algorithm> // 包含 swap
using namespace std;
// 泛型小根堆 struct
template <typename T>
struct MinHeap {
// 使用 vector 省去手动管理数组大小的麻烦
vector<T> h;
// 构造函数:初始化塞入一个占位符,保证下标从 1 开始
MinHeap() {
h.push_back(T());
}
// 【核心 1:上浮】
// 只要比爸爸小,就跟爸爸换位置
void up(int u) {
while (u > 1 && h[u] < h[u / 2]) {
swap(h[u], h[u / 2]);
u /= 2;
}
}
// 【核心 2:下沉】
// 找出 (自己, 左孩子, 右孩子) 里最小的,只要最小的不是自己,就交换下去
void down(int u) {
int t = u; // t 代表“最小值的下标”
int left = u * 2; // 左孩子
int right = u * 2 + 1; // 右孩子
// 如果左孩子存在,且比当前最小值还小,更新 t
if (left < h.size() && h[left] < h[t]) t = left;
// 如果右孩子存在,且比当前最小值还小,更新 t
if (right < h.size() && h[right] < h[t]) t = right;
// 如果最小值不是自己,说明需要交换并继续下沉
if (t != u) {
swap(h[u], h[t]);
down(t); // 递归下沉
}
}
// --- 用户接口 ---
// 插入 x
void push(T x) {
h.push_back(x); // 1. 放到数组末尾
up(h.size() - 1); // 2. 上浮到正确位置
}
// 删除堆顶
void pop() {
if (size() == 0) return;
swap(h[1], h.back()); // 1. 拿末尾元素覆盖堆顶
h.pop_back(); // 2. 删除末尾
if (size() > 0) { // 3. 堆顶下沉
down(1);
}
}
// 获取堆顶
T top() {
return h[1];
}
// 获取元素个数
int size() {
return h.size() - 1; // 减去那个占位符
}
bool empty() {
return size() == 0;
}
};
// --- 主程序 ---
int main() {
// 1. IO 优化
ios::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
// 2. 实例化 struct (int 类型)
MinHeap<int> heap;
while (n--) {
int op;
cin >> op;
if (op == 1) {
int x;
cin >> x;
heap.push(x);
} else if (op == 2) {
cout << heap.top() << "\n";
} else if (op == 3) {
heap.pop();
}
}
return 0;
}