堆支持的操作
维护一个数据集合
- 插入一个数
- 求这个集合的最值
- 删除最值
- 删除任意一个元素
- 修改任意一个元素
堆本质上是一棵完全二叉树,最后一层节点从左到右依次排布的
以小根堆为例,每一个点都是小于等于左右儿子的
堆的存储
用一维数组
1为根节点,x节点的左儿子是2x2x2x右儿子是2x+12x+12x+1
操作
基本操作
O(logN)O(logN)O(logN)N是层数
// up 上移一个节点
void up(int u){
while(u / 2 && h[u / 2] > h[u]){
h_swap(u / 2, u);
u /= 2;
}
}
// down 下放一个节点
void down(int u){
int t = u;
if(u << 1 <= size && h[u << 1] < h[t]) t = (u << 1);
if(u << 1 | 1 <= size && h[u << 1 | 1] < h[t]) t = (u << 1 | 1);
if(t != u){
// 如果t不相等意味着u还不满足小堆的条件于最小儿子交换后继续down,这里要结合定义去理解,
//这说明我们现在的这个元素,比两边都大,需要把他往下放,放到两节点最小的那个上去
//又因为他比较大,也得看看是不是能把他再往下放放
h_swap(t, u);
down(t);
}
}
扩展实现
插入一个元素
在二叉树的最底端插入这个数,然后不断往上移
void push(int x){
heap[++ size] = x;
up(size);
}
求当前集合当中的最小值
int top(){
return heap[1];
}
删除一个最小值
用最后一个元素来覆盖到根,然后再down一遍
void pop(){
heap[1] = heap[size];
size--;
down(1);
}
删除任意一个元素
void pop_any(int k){
heap[k] = heap[size];
size--;
down(k); // 只有变大了才会往下,只有变小的才会往上,所以这两种操作实际上有一个是不会执行的
up(k);
}
修改任意一个元素
void modify_any(int k, int x){
heap[k] = x;
down(k); // 只有变大了才会往下,只有变小的才会往上,所以这两种操作实际上有一个是不会执行的
up(k);
}
结构体版
struct heap{
int h[100000 + 10];
int ph[100000 + 10]; // 记录第k次插入的数在堆的哪里
int hp[100000 + 10]; // 找到当前这个数对应的ph是哪里,就是找到这个数是第几次插入的
int size;
int idx; // 第几个插入的元素
void h_swap(int a, int b){
swap(ph[hp[a]], ph[hp[b]]); // 通过hp[a]找回去ph
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(t != u){
h_swap(t, u);
down(t);
}
}
void up(int u){
while(u / 2 && h[u / 2] > h[u]){
h_swap(u / 2, u);
u /= 2;
}
}
void init(){
for(int i = n / 2; i; i--){
down(i);
}
}
void push(int x){
h[++size] = x;
idx++; // 看是第几次插入的
ph[idx] = size;
hp[size] = idx;
up(size);
}
void pop(){
if(size){
h_swap(1, size);
size--;
down(1);
}
}
int top(){
return h[1];
}
void pop_any(int k){ // 删除第k次的元素
k = ph[k];
h_swap(k, size);
size--;
down(k);
up(k);
}
void modify_any(int k, int x){ // 修改第k次插入的元素
k = ph[k];
h[k] = x;
down(k);
up(k);
}
}h;
1818

被折叠的 条评论
为什么被折叠?



