#include<iostream>usingnamespace std;constint N =1e5+10;// h代表堆,h[i]代表第i个节点的值; h_size代表堆的大小; 要取最小值,是小根堆// ph[i]代表第i个输入元素在堆中的下标// hp[i]代表编号为i的节点是第几个插入的int h[N], h_size, ph[N], hp[N];// 交换堆中编号为i和编号为j的两个节点voidheap_swap(int i,int j){swap(ph[hp[i]], ph[hp[j]]);swap(hp[i], hp[j]);swap(h[i], h[j]);}// 将编号为i的节点上升到堆的合适位置voidup(int i){// i指向孩子节点,判断孩子节点的值是不是小于父节点,如果小于就交换两个节点while(i /2&& h[i]< h[i /2]){heap_swap(i, i /2);
i /=2;}}// 将编号为i的节点下降到堆的合适位置voiddown(int i){// i和初始t都指向父节点; t维护父节点和左右孩子中值最小的节点的编号int t = i;if(2* i <= h_size && h[2* i]< h[t]) t =2* i;if(2* i +1<= h_size && h[2* i +1]< h[t]) t =2* i +1;// 如果最小的节点不是父节点,交换父节点和孩子节点if(t != i){heap_swap(i, t);down(t);}}intmain(){int n;
cin >> n;// count记录这是第几个插入的元素int count =0;while(n --){
string op;
cin >> op;if(op =="I"){int x;
cin >> x;// 在堆尾插入一个数,然后up到堆的合适位置
h[++ h_size]= x;
ph[++ count ]= h_size, hp[h_size]= count;up(h_size);}elseif(op =="PM"){
cout << h[1]<< endl;}elseif(op =="DM"){// 使用堆尾的节点覆盖堆顶,再将新堆顶down到堆的合适位置heap_swap(h_size,1);
h_size --;down(1);}elseif(op =="D"){int k;
cin >> k;// 先得到第k个插入的元素在堆中的编号iint i = ph[k];// 再用堆尾节点覆盖该编号的节点,然后再up/down一下(up和down只会执行其中之一)heap_swap(h_size, i);
h_size --;up(i),down(i);}elseif(op =="C"){int k, x;
cin >> k >> x;// 先得到第i个插入节点在堆中的编号int i = ph[k];// 修改该编号的节点的值
h[i]= x;// 该完值后再up/down一下,让该节点调整到堆中合适的位置up(i),down(i);}}return0;}
5. 应用:堆排序——找出数组中前m个最小元素:
#include<iostream>usingnamespace std;constint N =1e5+10;// h数组表示堆,从1开始编号,h[1]代表堆中编号为1的节点的值// h_size表示堆的大小int h[N], h_size;// 当编号为i的节点的值大于左右孩子时,下沉该节点voiddown(int i){// t指针始终指向父节点和左右孩子中值最小的节点// i是父节点; t一开始指向父节点int t = i;// 先判断有没有左右孩子,再将值最小的节点的编号用t保存if(2* i <= h_size && h[2* i]< h[t]) t =2* i;if(2* i +1<= h_size && h[2* i +1]< h[t]) t =2* i +1;if(t != i){swap(h[i], h[t]);down(t);}}intmain(){int n, m;
cin >> n >> m;// 初始堆for(int i =1; i <= n; i ++) cin >> h[i];// 构造小根堆; 从n/2节点开始down; down完后堆顶是最小元素
h_size = n;for(int i = n /2; i >=1; i --)down(i);while(m --){
cout << h[1]<<" ";// 用堆中最后一个节点覆盖堆顶,相当于删掉了堆顶
h[1]= h[h_size];
h_size --;// 再down新堆顶down(1);}return0;}