堆排序(如何手写堆)
文章目錄
- 1.一道算法題(堆排序模板題)
- 2.數組模擬小根堆的原理
- 3.浙大PAT算法題:堆排序還是插入排序
1.一道算法題(堆排序模板題)
acwing838. 堆排序
838. 堆排序
分析:
這是一道堆排序的模板題目,讓輸出前k小的元素,典型的小根堆的應用問題。
所以這道題目的核心就是建立小根堆 和動態維護堆的過程。
ac代碼
#include<bits/stdc++.h> using namespace std;const int N= 1e5+10; int n,m; int h[N],size1; //h就是堆數組,size表示當前堆的大小void down(int u ){ //u是下標int t=u; //表示三個點中的最小值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){ //如果最小值不是根結點,而是兒子結點swap(h[t],h[u]); //交換之down(t); //以新的根結點(兒子結點)往下接著判斷建堆}} 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.數組模擬小根堆的原理
以小根堆為例介紹堆。堆首先是一棵完全二叉樹,并且每個結點的值都小于等于左、右兒子的值。這樣得到根結點就是整個堆的最小值。
堆用一維數組來存,根結點存在下標1處。下標0不使用。某結點下標為i,則左兒子下標為2i ,右兒子下標為2i+1(如果兒子存在的話)。實際上,這就是完全二叉樹的性質。
我們用heap表示一維數組建的堆,size表示當前堆的大小。
然后我們需要寫兩個函數,down()和up()分別用來動態調整,使之成為堆。down()函數的意思是,堆頂(或者靠上的位置)來了一個較大的數,需要往下調整,即下沉; up()函數的意思是,堆底部(或者靠下的位置)來了一個較小的數,需要往上調整,即上浮。
遞歸實現的down函數:
void down(int u ){ //u是下標int t=u; //表示三個點中的最小值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){ //如果根結點和兒子結點交換了swap(h[t],h[u]);down(t); //以t為根結點遞歸建堆}}遞歸實現的up函數
void up(int u){int t =u; //三個結點中的最大值的下標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操作實現。
在數組最后的位置插入一個數,然后使用up函數往上調整,重新調整成堆。
這里是小根堆,堆頂元素就是最小值
刪除的思路:用數組最后的元素覆蓋堆頂元素(這代表刪除),然后使用down函數向下調整,使之滿足堆的性質。
刪除任意元素的思路:該元素用堆末尾元素覆蓋,然后向上調整一遍或者向下調整一遍,使之成堆。
堆排序就是把數組建成堆,每次把堆頂輸出,如果是小根堆,每次輸出最小值,這樣就是從小到大排序。如果是大根堆,每次輸出最大值,這樣就是大根堆。
下面補充大根堆的建堆和從大到小排序代碼
使用up函數建立大根堆,只需要遍歷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就是堆數組,size表示當前堆的大小void up(int u){int t =u; //三個結點中的最大值的下標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甲級1098 Insertion or Heap Sort:[C++題解]堆排序和插入排序
ac代碼
#include<bits/stdc++.h> using namespace std;const int N=110;int n ,a[N],b[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) { //此結點不是最大的,交換,并且遞歸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];}總結
以上是生活随笔為你收集整理的堆排序(如何手写堆)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PAT甲级1098 Insertion
- 下一篇: 并查集板子:acwing836. 合并集