堆排序
堆:
#include <cstdio> #include <iostream> using namespace std; const int N = 100010; int n,m,h[N],s; void down(int u) { int t = u; if(u * 2 <= s && h[u*2] < h[t]) t = 2 * u; if(u * 2 + 1 <= s && h[u * 2 + 1] < h[t]) t = 2 * u + 1; if(u != t) { swap(h[u],h[t]); down(t); } } int main() { cin>>n>>m; for(int i = 1;i <= n;i++) cin>>h[i]; s = n; for(int i = n/2;i;i--) down(i); while(m--) { cout<<h[1]<<" "; h[1] = h[s--]; down(1); } }
STL当中有优先队列:priority_queue。
本题:输入一个长度为n的整数数列,从小到大输出前m小的数。是用小根堆,手写堆,根小于等于左右子结点,堆顶是最小值。
存储:放在一个一维数组里面,根节点为1(x),左子结点为2(2 * x),右子结点为3(2 * x + 1). 1 2 3 4 5 6 7 8 9 ……
down(x):当把值变大时,需要将他的位置移动到合适的地方,下移。
up(x):当把值变小时,需要将他的位置移动到合适的地方,上移。
插入一个数:heap[++size] = x, up(x);
求出最小值:heap(1);
删除最小数:因为存储方式是一维数组,删最后的方便,只需要size--即可,头中间不好删,所以先将最后的数替换到第1个结点,heap(1) = heap[size]; size--;down(1);
删除任意一个元素: heap[k] = heap[size--]; down(k);
修改任意元素:heap[k] = x; up(x) OR down(x);
本题是默认先把数组里面所有的元素放在一维数组,然后对应到二叉堆里面,然后递归循环通过down操作来使得每个元素归位放在合适的位置。
for(int i = n/2 ;i ; i--) down(i); 因为对于一个满二叉树来说,最后一层个数为n/2,根节点到倒数第二层的总数也为n/2,下移的层数为第一层到倒数第二层,个数为n/2。
这里操作的次数小于n: S = n/4 + 2 * n/ 8 + 3 * n/16 + …… 2S = n/2 + 2 * n /4 + 3 * n / 8 …… 后面减去前面的S = n/2 + n/4 +n/8 + …… = n * 1/2 (1 - 1/(2^n)/(1-1/2) = n * (1 - 1 / 2 ^ n) < n; 所以总的down次数为小于O(n)。
此题还可以用STL的有限队列来写:
priority_queue<int , vector<int>, greater<int> > q;
#include <iostream> #include <queue> using namespace std; const int N = 100010; int a[N]; priority_queue<int, vector<int>, greater<int> >q;//小根堆,less是大根堆,可以认识是根以下的更小,根最大所以是大根堆,greater同理 int main() { int n,m; cin>>n>>m; for(int i = 0;i<n;i++) { cin>>a[i]; q.push(a[i]); } while(m--) { cout<<q.top()<<" "; q.pop(); } return 0; }
模拟堆:
维护一个集合,初始时集合为空,支持如下几种操作:
- “I x”,插入一个数x;
- “PM”,输出当前集合中的最小值;
- “DM”,删除当前集合中的最小值(数据保证此时的最小值唯一);
- “D k”,删除第k个插入的数;
- “C k x”,修改第k个插入的数,将其变为x;
#include <cstdio> #include <iostream> #include <string.h> using namespace std; const int N = 100010; int h[N],ph[N],hp[N],s;//ph[k]第k个插入的点,hp[k]堆上对应的ph[]数组。 ph[j] = k; hp[k] = j; void heap_swap(int a, int b) { swap(ph[hp[a]], ph[hp[b]]); swap(hp[a],hp[b]); swap(h[a],h[b]); } void down(int u) { int t = u; if(u * 2 <= s && h[u*2] < h[t]) t = 2 * u; if(u * 2 + 1 <= s && h[u * 2 + 1] < h[t]) t = 2 * u + 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; cin>>n; while(n--) { char op[10]; int k,x; scanf("%s",op); if(!strcmp(op,"I")) { cin>>x; s++; m++; ph[m] = s; hp[s] = m; h[s] = x; up(s); } else if(!strcmp(op,"PM")) { cout<<h[1]<<endl; } else if(!strcmp(op, "DM")) { heap_swap(1, s); s--; down(1); } else if(!strcmp(op, "D")) { cin>>k; k = ph[k]; heap_swap(k, s); s--; down(k), up(k); } else{ cin>>k>>x; k = ph[k]; h[k] = x; down(k), up(k); } } }