一. 模板
- 模拟堆
// h[N]存储堆中的值, h[1]是堆顶,x的左儿子是2x, 右儿子是2x + 1
// ph[k]存储第k个插入的点在堆中的位置
// hp[k]存储堆中下标是k的点是第几个插入的
int h[N], ph[N], hp[N], size;
// 交换两个点,及其映射关系
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 <= size && h[u * 2] < h[t]) t = u * 2;
if (u * 2 + 1 <= size && 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] < h[u / 2])
{
heap_swap(u, u / 2);
u >>= 1;
}
}
// O(n)建堆
for (int i = n / 2; i; i -- ) down(i);
- 堆排序
int h[N], cnt; // h[N]是堆、cnt是树的结点数
// 大的数向下调整
void down(int u)
{
int t = u; // 临时记录u的值,t代表最小结点
if (2 * u <= cnt && h[t] > h[2 * u]) t = 2 * u; // 有左子节点,先比较左子节点
if (2 * u + 1 <= cnt && h[t] > h[2 * u + 1]) t = 2 * u + 1; // 有右子节点,再比较右子节点
if (t != u) {
swap(h[t], h[u]); // 如果有变化,就交换该位置和三个结点中最大的位置
down(t); // 继续down
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> h[i]; // 从1开始,方便确认子节点位置
cnt = n;
// 将数组构造成堆, 从树的倒数第二行开始down
for (int i = n / 2; i ; --i) down(i);
while (m--) {
cout << h[1] << ' ';
// 删除最小值
h[1] = h[cnt];
down(1);
--cnt;
}
}
二. 总结
- 如何手写堆?
- 堆排序中如何将数组构造成堆中?
三. 例题
AC代码:
#include <iostream>
using namespace std;
const int N = 1e5 +10;
int h[N], ph[N], hp[N], cnt;
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 (2 * u <= cnt && h[t] > h[2 * u]) t = 2 * u;
if (2 * u + 1 <= cnt && h[t] > h[2 * u + 1]) t = 2 * u + 1;
if (u != t) {
heap_swap(u, t);
down(t);
}
}
void up(int u)
{
while (u / 2 && h[u] < h[u / 2]) {
heap_swap(u, u / 2); // 交换u和u的父节点
u >>= 1; // u向上移动
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
string op;
int k, x, idx = 0; // idx 用于记录是第几个插入的
while (n--) {
cin >> op;
if (op == "I") {
cin >> x;
++cnt, ++idx;
h[cnt] = x;
ph[idx] = cnt;
hp[cnt] = idx;
up(cnt);
}
else if (op == "PM") cout << h[1] << endl;
else if (op == "DM") {
heap_swap(1, cnt);
--cnt;
down(1);
}
else if (op == "D") {
cin >> k;
k = ph[k]; // 从加入的索引映射到树中的位置
heap_swap(k, cnt);
--cnt;
down(k);
up(k);
}
else {
cin >> k >> x;
k = ph[k]; // 从加入的索引映射到树中的位置
h[k] = x;
down(k);
up(k);
}
}
return 0;
}