-
手写堆需要支持的操作
- 插入一个数
- 求集合当中的最小值
- 删除最小值
- 删除任意一个元素
- 修改任意一个元素
-
堆是一棵完全二叉树
-
大根堆和小根堆的特点
-
存储方式
- 用一个一维数组来存储
- 位置1存储根节点(下标从0开始需要特殊出根节点,因为0 * 2 == 0)
- 对于节点x,左儿子的下标为2x,右儿子的下标为2x++1
-
堆支持两个基本的操作,这两个基本操作可以组合出前面提出的操作
- down()
- up()
-
以小根堆为例,如果将某个节点的值变大,为了维护小根堆的性质,需要将当前节点"下沉"几次,直到整棵树满足小根堆的性质;如果将某个节点的值变小,需要将当前节点“上浮”几次,直到整棵树重新满足最小堆的性质
-
用两个基本操作模拟对一个堆可以进行的操作(以小顶堆为例)
- heap[++ size] = x; up(size);
- heap[1];
- heap[1] = heap[size]; size --; down(1);
- heap[k] = heap[size]; size --; up(k) 或 down[k]
- heap[k] = x; down[x] 或 up[x];
#include <iostream>
#include <algorithm>
#include <string.h> //使用strcmp必须包含这个头文件
using namespace std;
const int N = 100010;
int h[N], ph[N], hp[N], h_size; //ph存储第k个插入点的下标j,hp存储的是下标为j的元素是第k个插入的点,互为反映射
void heap_swap(int a, int b) {
swap(ph[hp[a]], ph[hp[b]]);
swap(hp[a], hp[b]);
swap(h[a], h[b]);
}//为了实现操作4和操作5而定义的特殊的swap操作,大多数情况下不需要实现这些复杂的交换操作
void down(int u) {
int t = u;
if(u * 2 <= h_size && h[u * 2] < h[t]) t = u * 2;
if(u * 2 + 1 <= h_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 / 2] > h[u]) {
heap_swap(u / 2, u);
u /= 2;
}
}
int main() {
int n, m = 0;
scanf("%d", &n);
while(n -- ) {
char op[10];
int k, x;
scanf("%s", op);
if (!strcmp(op, "I")) {
scanf("%d", &x);
h_size ++ ;
m ++ ;
ph[m] = h_size; hp[h_size] = m;
h[h_size] = x;
up(h_size);
}
else if (!strcmp(op, "PM")) printf("%d
", h[1]);
else if (!strcmp(op, "DM")) {
heap_swap(1, h_size);
h_size -- ;
down(1);
}
else if (!strcmp(op, "D")) {
scanf("%d", &k);
k = ph[k];
heap_swap(k, h_size);
h_size -- ;
down(k), up(k);
}
else {
scanf("%d%d", &k, &x);
k = ph[k];
h[k] = x;
down(k), up(k);
}
}
return 0;
}
相关题目 堆排序