日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

堆排序(如何手写堆)

發布時間:2025/4/5 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 堆排序(如何手写堆) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 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操作實現。

  • 插入一個數 heap[++size] =x ,up(size);
    在數組最后的位置插入一個數,然后使用up函數往上調整,重新調整成堆。
  • 求集合中的最小值 heap[1]
    這里是小根堆,堆頂元素就是最小值
  • 刪除最小值heap[1] =heap[size]; size--;down(size);
    刪除的思路:用數組最后的元素覆蓋堆頂元素(這代表刪除),然后使用down函數向下調整,使之滿足堆的性質。
  • 刪除任意一個下標為k的元素 heap[k]= heap[size]; size--;down(size),up(size);
    刪除任意元素的思路:該元素用堆末尾元素覆蓋,然后向上調整一遍或者向下調整一遍,使之成堆。
  • 修改任意一個元素 heap[k] =x; down(k),up(k);
  • 堆排序就是把數組建成堆,每次把堆頂輸出,如果是小根堆,每次輸出最小值,這樣就是從小到大排序。如果是大根堆,每次輸出最大值,這樣就是大根堆。

    下面補充大根堆的建堆和從大到小排序代碼

    使用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];}

    總結

    以上是生活随笔為你收集整理的堆排序(如何手写堆)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。