堆排序(如何手写堆)
文章目錄
- 1.一道算法題(堆排序模板題)
- 2.數(shù)組模擬小根堆的原理
- 3.浙大PAT算法題:堆排序還是插入排序
1.一道算法題(堆排序模板題)
acwing838. 堆排序
838. 堆排序
分析:
這是一道堆排序的模板題目,讓輸出前k小的元素,典型的小根堆的應(yīng)用問(wèn)題。
所以這道題目的核心就是建立小根堆 和動(dòng)態(tài)維護(hù)堆的過(guò)程。
ac代碼
#include<bits/stdc++.h> using namespace std;const int N= 1e5+10; int n,m; int h[N],size1; //h就是堆數(shù)組,size表示當(dāng)前堆的大小void down(int u ){ //u是下標(biāo)int t=u; //表示三個(gè)點(diǎn)中的最小值if(u*2<=size1 && h[t]>=h[u*2]) t =2*u;if(u*2+ 1<= size1 && h[t]>= h[u*2+1] ) t=2*u +1;if(t != u){ //如果最小值不是根結(jié)點(diǎn),而是兒子結(jié)點(diǎn)swap(h[t],h[u]); //交換之down(t); //以新的根結(jié)點(diǎn)(兒子結(jié)點(diǎn))往下接著判斷建堆}} int main(){cin>> n>>m;for(int i=1;i<=n;i++) cin>>h[i];size1 =n;//O(n)的建堆方式for(int i=n/2;i;i--) down(i); //從n/2 down到1while(m--){cout<<h[1]<<" ";h[1]=h[size1];size1--;down(1);} }2.數(shù)組模擬小根堆的原理
以小根堆為例介紹堆。堆首先是一棵完全二叉樹(shù),并且每個(gè)結(jié)點(diǎn)的值都小于等于左、右兒子的值。這樣得到根結(jié)點(diǎn)就是整個(gè)堆的最小值。
堆用一維數(shù)組來(lái)存,根結(jié)點(diǎn)存在下標(biāo)1處。下標(biāo)0不使用。某結(jié)點(diǎn)下標(biāo)為i,則左兒子下標(biāo)為2i ,右兒子下標(biāo)為2i+1(如果兒子存在的話)。實(shí)際上,這就是完全二叉樹(shù)的性質(zhì)。
我們用heap表示一維數(shù)組建的堆,size表示當(dāng)前堆的大小。
然后我們需要寫(xiě)兩個(gè)函數(shù),down()和up()分別用來(lái)動(dòng)態(tài)調(diào)整,使之成為堆。down()函數(shù)的意思是,堆頂(或者靠上的位置)來(lái)了一個(gè)較大的數(shù),需要往下調(diào)整,即下沉; up()函數(shù)的意思是,堆底部(或者靠下的位置)來(lái)了一個(gè)較小的數(shù),需要往上調(diào)整,即上浮。
遞歸實(shí)現(xiàn)的down函數(shù):
void down(int u ){ //u是下標(biāo)int t=u; //表示三個(gè)點(diǎn)中的最小值if(u*2<=size1 && h[t]>=h[u*2]) t =2*u;if(u*2+ 1<= size1 && h[t]>= h[u*2+1] ) t=2*u +1;if(t != u){ //如果根結(jié)點(diǎn)和兒子結(jié)點(diǎn)交換了swap(h[t],h[u]);down(t); //以t為根結(jié)點(diǎn)遞歸建堆}}遞歸實(shí)現(xiàn)的up函數(shù)
void up(int u){int t =u; //三個(gè)結(jié)點(diǎn)中的最大值的下標(biāo)if(2*u <=size1 && h[2*u]> h[t]) t=2*u;if(2*u+1<=size1 && h[2*u+1]> h[t]) t=2*u+1;if(t!= u){swap(h[t],h[u]);up(t);} }堆的常用操作
這些操作都可以使用up操作或者 down操作實(shí)現(xiàn)。
在數(shù)組最后的位置插入一個(gè)數(shù),然后使用up函數(shù)往上調(diào)整,重新調(diào)整成堆。
這里是小根堆,堆頂元素就是最小值
刪除的思路:用數(shù)組最后的元素覆蓋堆頂元素(這代表刪除),然后使用down函數(shù)向下調(diào)整,使之滿足堆的性質(zhì)。
刪除任意元素的思路:該元素用堆末尾元素覆蓋,然后向上調(diào)整一遍或者向下調(diào)整一遍,使之成堆。
堆排序就是把數(shù)組建成堆,每次把堆頂輸出,如果是小根堆,每次輸出最小值,這樣就是從小到大排序。如果是大根堆,每次輸出最大值,這樣就是大根堆。
下面補(bǔ)充大根堆的建堆和從大到小排序代碼
使用up函數(shù)建立大根堆,只需要遍歷for(int i=1;i<=n/2;i++) up(i);
堆排序從大到小排序:
#include<bits/stdc++.h> using namespace std;const int N= 1e5+10; int n,m; int h[N],size1; //h就是堆數(shù)組,size表示當(dāng)前堆的大小void up(int u){int t =u; //三個(gè)結(jié)點(diǎn)中的最大值的下標(biāo)if(2*u <=size1 && h[2*u]> h[t]) t=2*u;if(2*u+1<=size1 && h[2*u+1]> h[t]) t=2*u+1;if(t!= u){swap(h[t],h[u]);up(t);} } int main(){cin>> n>>m;for(int i=1;i<=n;i++) cin>>h[i];size1 =n;//O(n)的建堆方式for(int i=1;i<=n/2;i++) up(i);while(m--){cout<<h[1]<<" ";h[1]=h[size1--];up(1);}}3.浙大PAT算法題:堆排序還是插入排序
1588. 插入還是堆排序
筆者這道題ac的博客:PAT甲級(jí)1098 Insertion or Heap Sort:[C++題解]堆排序和插入排序
ac代碼
#include<bits/stdc++.h> using namespace std;const int N=110;int n ,a[N],b[N];//每次判斷當(dāng)前這個(gè)點(diǎn)是不是比左右兒子都小 void down(int u, int size ){int t = u;if(u*2 <= size && b[t]< b[u*2]) t =2*u;if(u*2+1 <=size && b[t]<b[u*2+1]) t =u*2+1;if(t !=u) { //此結(jié)點(diǎn)不是最大的,交換,并且遞歸swap(b[t],b[u]);down(t,size);}}int main(){cin>>n;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++) cin>>b[i];int p=2;while(p<=n && b[p-1]<=b[p]) p++;int k =p,flag=0; //0表示插入排序;1表示堆排序for(int i=p;i<=n;i++){if(a[i]!=b[i]){flag=1;break;} } if(!flag){//插入排序cout<<"Insertion Sort"<<endl;for(int i=k;i>1;i--)if(b[i-1]>b[i]) swap(b[i],b[i-1]);} else{cout<<"Heap Sort"<<endl;int k1 =n;while(b[k1]>b[1]) k1--;//b[1]是堆頂swap(b[1],b[k1]);down(1,k1-1);}cout<<b[1];for(int i=2;i<=n;i++) cout<<" "<<b[i];}總結(jié)
以上是生活随笔為你收集整理的堆排序(如何手写堆)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: PAT甲级1098 Insertion
- 下一篇: PAT甲级1004 Counting L