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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

堆排序(如何手写堆)

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

文章目錄

    • 1.一道算法題(堆排序模板題)
    • 2.數(shù)組模擬小根堆的原理
    • 3.浙大PAT算法題:堆排序還是插入排序

1.一道算法題(堆排序模板題)

acwing838. 堆排序
838. 堆排序

分析:
這是一道堆排序的模板題目,讓輸出前k小的元素,典型的小根堆的應(yīng)用問題。

所以這道題目的核心就是建立小根堆 和動(dòng)態(tài)維護(hù)堆的過程。

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ù)組模擬小根堆的原理

以小根堆為例介紹堆。堆首先是一棵完全二叉樹,并且每個(gè)結(jié)點(diǎn)的值都小于等于左、右兒子的值。這樣得到根結(jié)點(diǎn)就是整個(gè)堆的最小值。

堆用一維數(shù)組來存,根結(jié)點(diǎn)存在下標(biāo)1處。下標(biāo)0不使用。某結(jié)點(diǎn)下標(biāo)為i,則左兒子下標(biāo)為2i ,右兒子下標(biāo)為2i+1(如果兒子存在的話)。實(shí)際上,這就是完全二叉樹的性質(zhì)。

我們用heap表示一維數(shù)組建的堆,size表示當(dāng)前堆的大小。

然后我們需要寫兩個(gè)函數(shù),down()和up()分別用來動(dòng)態(tài)調(diào)整,使之成為堆。down()函數(shù)的意思是,堆頂(或者靠上的位置)來了一個(gè)較大的數(shù),需要往下調(diào)整,即下沉; up()函數(shù)的意思是,堆底部(或者靠下的位置)來了一個(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)。

  • 插入一個(gè)數(shù) heap[++size] =x ,up(size);
    在數(shù)組最后的位置插入一個(gè)數(shù),然后使用up函數(shù)往上調(diào)整,重新調(diào)整成堆。
  • 求集合中的最小值 heap[1]
    這里是小根堆,堆頂元素就是最小值
  • 刪除最小值heap[1] =heap[size]; size--;down(size);
    刪除的思路:用數(shù)組最后的元素覆蓋堆頂元素(這代表刪除),然后使用down函數(shù)向下調(diào)整,使之滿足堆的性質(zhì)。
  • 刪除任意一個(gè)下標(biāo)為k的元素 heap[k]= heap[size]; size--;down(size),up(size);
    刪除任意元素的思路:該元素用堆末尾元素覆蓋,然后向上調(diào)整一遍或者向下調(diào)整一遍,使之成堆。
  • 修改任意一個(gè)元素 heap[k] =x; down(k),up(k);
  • 堆排序就是把數(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甲級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é)

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

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。