操作:

  1. 插入一个数
  2. 求集合中的最大(小)值
  3. 删除集合的最大(小)值
  4. 删除任意一个元素
  5. 修改任意一个元素

以下以小根堆(根节点为最小值为例)

基本结构:二叉树

存储:用一个数组$heap$存储二叉树,$size$表示数组中元素的个数,$heap[1]$为根节点,对于一个节点$i$,$i2$是它的左儿子,$i2+1$是它的右儿子。

性质:对于每一个非根节点,它的值一定大于其父节点值

例:

此时的$heap$数组:

$i$ $1$ $2$ $3$ $4$ $5$ $6$ $7$ $8$ $9$ $10$
$heap[i]$ $1$ $2$ $3$ $4$ $5$ $6$ $7$ $8$ $9$ $10$

操作:

  1. $down(x)$:将$x$号节点的值下移直至二叉树满足堆的性质

    • 若两个子节点都比当前节点大或当前节点为叶子节点,则操作停止
    • 否则,将当前节点与两个子节点的较小值进行交换,重复上一步骤
  2. $down(x)$:将$x$号节点的值下移直至二叉树满足堆的性质

    • 若父节点比当前节点大或当前节点为根节点,则操作停止
    • 否则,将当前节点与父节点的值进行交换,重复上一步骤

  3. $insert(x)$:插入一个数$x$

    • 新建一个节点,其值为插入的数heap[++size]=x;
    • 将新插入的数上移up(size);
  4. $top()$:查找最小值:return heap[1];

  5. $pop()$:删除最小值

    • 将最小值和最后一个值交换swap(heap[1],heap[size]),然后删除最后一个值size--;,将此时的新根节点下移down(1);

    以下三个操作需要额外定义$ph,hp$数组,其中$ph[i]$表示第$i$个插入的数在堆中的下标,$hp[i]$表示堆中的第$i$号点是第几个插入的点,,交换时需交换所有数组,插入时也需记录次序。

  6. $heap_swap(x,y)$:交换两个点

    • 交换两点的$ph$(swap(ph[hp[x]],ph[hp[y]]))、$hp$(swap(hp[x],hp[y]))、值(swap(heap[x],heap[y])

  7. $del(k)$:删除第$k$个插入的数

    • 将当前数和最后一个值交换heap_swap(heap[k],heap[size]),然后删除最后一个值size--;,将此时的新的点下移且上移(不确定大小,都做一遍,只会执行一个)down(k);up(k);
  8. $change(k,x)$:把第$k$个插入的数改为$x$

    • 先修改heap[k]=x,然后将新点移动down(k);up(k);

代码实现:

普通版(不包含特定点的修改删除)

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
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int n,m;
int treap[N],cnt;

void up(int x){//上移
if(x==1)return;
if(treap[x>>1]>treap[x]){
swap(treap[x],treap[x>>1]);
up(x>>1);
}
}

void down(int x){//下移
int t=x;
if((x<<1)<=cnt&&treap[x<<1]<treap[t])t=x*2;
if((x<<1|1)<=cnt&&treap[x<<1|1]<treap[t])t=x*2+1;
if(x!=t){
swap(treap[x],treap[t]);
down(t);
}
}

void insert(int x){//插入
treap[++cnt]=x;
up(cnt);
}

void pop(){//弹出最小值
swap(treap[1],treap[cnt]);
cnt--;
down(1);
}

int top(){//询问最小值
return treap[1];
}

bool empty(){//询问是否为空
return !cnt;
}

int size(){//询问大小
return cnt;
}

int main(){
cin >> n >> m;
for(int i=1;i<=n;i++){
int tmp;
cin >> tmp;
insert(tmp);
}

for(int i=1;i<=m;i++){
cout << top() << ' ';
pop();
}

return 0;
}

进阶版(包含特定点操作,应用场景:$Dijkstra$):

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
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int n;
int heap[N],cnt,alls;
int ph[N],hp[N];

void heap_swap(int x,int y){//堆的交换
swap(ph[hp[x]],ph[hp[y]]);
swap(hp[x],hp[y]);
swap(heap[x],heap[y]);
}

void up(int x){//上移
if(x==1)return;
if(heap[x>>1]>heap[x]){
heap_swap(x,x>>1);
up(x>>1);
}
}

void down(int x){//下移
int t=x;
if((x<<1)<=cnt&&heap[x<<1]<heap[t])t=x*2;
if((x<<1|1)<=cnt&&heap[x<<1|1]<heap[t])t=x*2+1;
if(x!=t){
heap_swap(x,t);
down(t);
}
}

void insert(int x,int k){//插入
heap[++cnt]=x;
ph[k]=cnt;
hp[cnt]=k;
up(cnt);
}

void pop(){//弹出
heap_swap(1,cnt);
cnt--;
down(1);
}

int top(){//询问最小
return heap[1];
}

bool empty(){//询问是否为空
return !cnt;
}

int size(){//询问大小
return cnt;
}

void del(int k){//删除第k个插入的值
k=ph[k];
heap_swap(k,cnt);
cnt--;
up(k);down(k);
}

void change(int k,int x){//修改第k个插入的值
k=ph[k];
heap[k]=x;
up(k);down(k);
}

int main(){
cin >> n;
for(int i=1;i<=n;i++){
char op[3];
cin >> op;
if(op[0]=='I'){//插入
int x;
cin >> x;
insert(x,++alls);
}
else if(op[0]=='P'){//弹出最小值
cout << top() << endl;
}
else if(op[0]=='C'){//修改
int k,x;
cin >> k >> x;
change(k,x);
}
else if(op[1]=='M'){//弹出最小值
pop();
}
else{//删除
int k;
cin >> k;
del(k);
}
}
return 0;
}