基础算法--堆的结构及其常用操作和排序
堆,是一种完全二叉树的树状结构,分为小根堆与大顶堆。本文论述以及代码实现都是小根堆,大根堆同理。
如上图所示,我们可以看到一个完全二叉树,而每个结点的编号与其左右儿子的关系为(假设某结点的编号为u):left=2*u,right=2*u+1。
堆有如下的操作:
1.建堆
2.向下调整
3.向上调整
4.删除第一个(最小)元素
5.删除任意一个元素
6.插入某个元素
而2,3以后的操作全都可以用2,3两个操作组合完成。
小根堆的表述为:某个结点永远小于等于其左右儿子。那么我们就可以通过向下调整(大的往下沉)或向上(小的往上浮)调整来建立一个堆,代码如下:
void down(int u){//向下调整 int t=u; if(u*2<=n&&h[u*2]<h[t])t=u*2; if(u*2+1<=n&&h[u*2+1]<h[t])t=u*2+1; if(u!=t){ swap(h[u],h[t]); down(t); } } void up(int u){//向上调整 while(u/2&&h[u/2]>h[u]){ swap(h[u/2],h[u]); u/=2; } }
那么4,5,6的操作依次可以表示为:
h[1]=h[n],n--,down(1); h[k]=h[n],n--,down(k) or up(k); h[++n]=k,up(n);
下面是堆排序:
#include<iostream> #include<vector> #include<algorithm> using namespace std; const int N=1000010; vector<int>h(N),f(N); int n; void down(int u, int n){//向下调整 int t=u; if(u*2<=n&&h[u*2]<h[t])t=u*2; if(u*2+1<=n&&h[u*2+1]<h[t])t=u*2+1; if(u!=t){ swap(h[u],h[t]); down(t,n); } } void heap_sort(int n){ for(int i=n/2;i;i--)down(i,n); int i=1,k=n; while(k){ f[i++]=h[1]; h[1]=h[k--]; down(1, k); } } int main(void){ cin>>n; for(int i=1;i<=n;i++)cin>>h[i]; heap_sort(n); for(int i=1;i<=n;i++)cout<<f[i]<<' '; cout<<endl; return 0; }