排序算法--堆排序

算法簡介:

堆排序是利用堆這種數(shù)據(jù)結(jié)構(gòu)而設(shè)計的一種排序算法,堆排序是一種選擇排序,它的最壞,最好,平均時間復雜度均為(nlogn),它也是不穩(wěn)定排序,首先了解一下

堆是具有以下性質(zhì)的完全二叉樹:每個節(jié)點的值都大于或等于其左右孩子節(jié)點的值,稱為大頂堆;或者每個節(jié)點的值都小于或等于其左右孩子節(jié)點的值,稱為小頂堆。如圖:

1024555-20161217182750011-675658660.png

同時,我們對堆中的節(jié)點按層進行編號,將這種邏輯結(jié)構(gòu)映射到數(shù)組中就是下面這個樣子


1024555-20161217182857323-2092264199.png

接下來,我們來看看堆排序的基本思想及實現(xiàn)步驟:

堆排序的基本思想及實現(xiàn)步驟

堆排序的基本思想是:將待排序序列構(gòu)造成一個大頂堆,此時,整個序列的最大值就是堆頂?shù)母?jié)點。將其與末尾元素進行交換,此時末尾元素成為最大值。然后將剩余n-1個元素重新構(gòu)造成一個大頂堆,這樣會得到n個元素的次小值,此時根節(jié)點又會成為次小值中的最大值,再重復以上步驟,最終便會得到一個有序序列了。

步驟一 構(gòu)造初始大頂堆(升序用大頂堆,降序小頂堆)

上圖:
1.假定給定無序序列結(jié)構(gòu)如下


1024555-20161217192038651-934327647.png

2.此時我們從最后一個非葉子節(jié)點(6)開始,從左至右,從下至上進行調(diào)整


1024555-20161217192209433-270379236.png

3.找到第二個非葉子節(jié)點4,由于(4,9,8)中9最大,4和9進行交換。
1024555-20161217192854636-1823585260.png

這時,交換導致了子根(4,5,6)結(jié)構(gòu)的混亂,繼續(xù)調(diào)整,(4,5,6)中6最大。交換4和6.
1024555-20161217193347886-1142194411.png

此時,我們就將一個無序序列構(gòu)造成了一個大頂堆。

步驟二 將堆頂元素與末尾元素進行交換,使末尾元素最大。然后繼續(xù)調(diào)整堆(此時調(diào)整的堆元素長度為n-1,與大頂堆根元素交換過的位置在下一次調(diào)整排除)然后繼續(xù)調(diào)整堆,再將堆頂元素與末尾元素交換,得到第二大元素。如此反復進行交換、重建、交換。

上圖:
1.將堆頂元素9和末尾元素4交換:


1024555-20161217194207620-1455153342.png

2.重新調(diào)整結(jié)構(gòu),使剩下的n-1個元素繼續(xù)成為大頂堆

1024555-20161218153110495-1280388728.png

3.再將堆頂元素8和末尾元素5進行交換,得到第二大元素8


1024555-20161218152929339-1114983222.png

后續(xù)過程,進行重復以上步驟,最終使得整個序列有序


1024555-20161218152348229-935654830.png

對堆排序的基本思路的簡單總結(jié):
a.將無序序列構(gòu)建成一個堆,根據(jù)升序降序需求選擇大頂堆還是小頂堆;

b.將堆頂元素與末尾元素進行交換,將最大(最小)元素“沉”到數(shù)組末端;

c.重新調(diào)整剩余的n-1(n-2,n-3....1)個結(jié)構(gòu),使其滿足堆定義,然后繼續(xù)交換堆頂元素和當前末尾元素,反復執(zhí)行調(diào)整+交換步驟,直到整個序列有序。

代碼實現(xiàn):

public class Duition{
  public static void sort(int[] arr){
     int len = arr.length - 1;
    for(int i = len/2-1;i>=0;i--){
       //構(gòu)造大頂堆
       headAdjust(arr,i,len);
     }
    //交換根元素后再進行堆調(diào)整
   while(len>=0){
       swap(arr,0,len--);
       headAdjust(arr,0,len);
    }
  }

 //交換指定元素
  public static void swap(int[] arr,int i,int j){
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }



//構(gòu)造大頂堆
public static void headAdjust(int[] arr,int i,int len){
  int left; //左孩子
  int right;  //右孩子
  int j;
  
  while((left = i*2+1)<=len){
      right = left +1;
      j = left;
    
    //比較左右孩子,將其中較大者的位置賦予j
    if(j<len && arr[left] < arr[right]){
          j++;
    }
     //比較根節(jié)點和較大孩子的值
     if(arr[i] < arr[j])
        swap(arr,i,j);
     else
         breadk;

      i = j; //下一個根節(jié)點,為下一次循環(huán)做準備
     }
  }


}

其中 len/2-1是表示數(shù)組元素按曾編號之后非葉子節(jié)點的下標范圍,在len/2-1之后的元素都是葉子節(jié)點(即沒有左右孩子),這是完全二叉樹的規(guī)律。

注:以上算法思想及圖源自圖解排序算法(三)之堆排序,代碼已測試,沒發(fā)現(xiàn)什么問題,做個記錄,以便日后查詢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容