堆
支持的操作
- 插入一个数 h e a p [ + + s i z e ] = x ; u p ( s i z e ) heap[++size]=x;\ up(size) heap[++size]=x; up(size)
- 求这个集合中的最小值 h e a p [ 1 ] heap[1] heap[1]
- 删除最小值 (把堆的最后一个元素覆盖到堆顶,再 d o w n down down 堆顶)
h e a p [ 1 ] = h e a p [ s i z e ] ; s i z e − − ; d o w n ( 1 ) ; heap[1]=heap[size];\ size--;\ down(1); heap[1]=heap[size]; size−−; down(1);
原因是,删除数组中的头节点非常方便,删除尾节点非常困难
- 删除任意一个元素(手写)和删除根节点类似
h e a p [ k ] = h e a p [ s i z e ] ; s i z e − − ; d o w n ( k ) , u p ( k ) heap[k]=heap[size];\ size--; \ down(k),up(k) heap[k]=heap[size]; size−−; down(k),up(k)
down
up
只有一个会被执行
- 修改任意一个元素(手写)
h e a p [ k ] = x ; s i z e − − ; d o w n ( k ) , u p ( k ) heap[k]=x;\ size--; \ down(k),up(k) heap[k]=x; size−−; down(k),up(k)
down
up
只有一个会被执行
堆的特点
- 是一个平衡二叉树,除了最后一层的节点以外,其余所有节点都是满的,最后一层节点从左到右排列
- 每一个点都是小于等于它的左右儿子的(小根堆) → \to → 根节点一定是所有节点里的最小值
堆的存储
使用一个一维数组存储!
设 1 号点为根节点,则对于数组中任意下标为 x x x 的元素, x x x 的左儿子为 2 x 2x 2x , x x x 的右儿子为 2 x + 1 2x+1 2x+1
堆的基本操作
- d o w n ( x ) down(x) down(x) 往下调整
- u p ( x ) up(x) up(x) 往上调整
堆排序:https://www.acwing.com/problem/content/840/
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
#define debug(a) cout << #a << " " << a << endl
const int maxn = 1e5 + 7;
const int N = 1e6 + 7, M = N * 2;
const int inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;
int n, m;
int h[N], siz;
void down(int u) {
int t = u; //t存储的是三个点里面最小的节点编号
if(u * 2 <= siz && h[u * 2] < h[t]) t = u * 2;
if(u * 2 + 1 <= siz && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
if(u != t) { //如果顶点不是最小的
swap(h[u], h[t]);
down(t);
}
}
int main() {
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
// ios::sync_with_stdio(false);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &h[i]);
siz = n;
for(int i = n / 2; i > 0; i--) down(i);
while(m--) {
printf("%d ", h[1]);
h[1] = h[siz];
siz--;
down(1);
}
return 0;
}
/*
数组开够了吗 开到上界的n+1次方
初始化了吗
*/