1. 程式人生 > >堆排序學習筆記

堆排序學習筆記

一.基礎準備

        這個演算法研究了兩天,整整兩天啊,中間水了一道線段樹,好在皇天不負有心人,嘿嘿。

        1991年計算機先驅獎獲得者、斯坦福大學計算機科學系教授羅伯特·弗洛伊德(Robert W.Floyd)和威廉姆斯(J.Williams)在1964年共同發明了著名的堆排序演算法(Heap Sort )。堆排序是高效的排序方法。沒有最壞情況(即與平均情況一樣),空間佔用又小,綜合效率比快速排序還好。

        資料結構中的堆和作業系統中的堆、堆疊(棧)是沒有關係的,不要有誤解。

        說道堆排序就不得不先說堆,樹中任一非葉結點的關鍵字均不大於(或不小於)其左右孩子(若存在)結點的關鍵字,分別對應大頂堆和小頂堆。堆排序利用了大根堆(或小根堆)堆頂記錄的關鍵字最大(或最小)這一特徵,使得在當前無序區中選取最大(或最小)關鍵字的記錄變得簡單。

        堆排序(HeapSort)是一樹形選擇排序。堆排序的特點是:在排序過程中,將R[l..n]看成是一棵完全二叉樹的順序儲存結構,利用完全二叉樹中雙親結點和孩子結點之間的內在關係(參見二叉樹的順序儲存結構),在當前無序區中選擇關鍵字最大(或最小)的記錄。堆排序的最壞時間複雜度為O(nlogn)。堆序的平均效能較接近於最壞效能。由於建初始堆所需的比較次數較多,所以堆排序不適宜於記錄數較少的檔案。堆排序是就地排序,輔助空間為O(1),它是不穩定的排序方法。

        注意:①堆中任一子樹亦是堆。②以上討論的堆實際上是二叉堆(Binary

Heap),類似地可定義k叉堆。

        題外話:滿二叉樹肯定是完全二叉樹,完全二叉樹不一定是滿二叉樹;在C和彙編裡,位移運算比乘除要快得多。位移運算比加減更快,可能只佔一個CPU時鐘,乘除要佔20多個以上,差了一個數量級。效率而言,那是差不起的。

二.演算法理解

imageimage

imageimage

三.演算法實現

  1: //小頂堆
  2: public class W {
  3: 
  4:   public static void main(String[] args) {
  5:     //第一個元素不用
  6:     int a[] = {65535,49, 38, 65, 97, 76, 13, 27, 49};
  7:     System.out.print("排序前序列為:");
  8:     for(int i:a) {
  9:       System.out.print(i+"");
 10:     }
 11:     System.out.println();
 12:     mySort(a,a.length-1);
 13:     System.out.print("排序後序列為:");
 14:     for(int i:a) {
 15:       System.out.print(i+"");
 16:     }
 17:     System.out.println();
 18:   }
 19: 
 20:   private static void mySort(int[] a,int len) {
 21:     //構建小頂堆成功
 22:     for(int i=len/2; i>=1; i--) {
 23:       adjust(a,i,len);
 24: //      System.out.print(a[i]+" "+i+":");
 25: //      for(int j:a) {
 26: //        System.out.print(j+" ");
 27: //      }
 28: //      System.out.println();
 29:     }
 30:     //注意共len-1次迴圈
 31:     for(int i=1; i<len; i++) {
 32:       /*
 33:        * 注意下邊這兩句第二個引數都是1
 34:        * 這個for迴圈裡只需要調整一個節點就是根節點,而adjust初初始設計為
 35:        * 從最後開始,不過此時也行了,因為adjust雖然是從最後開始不過
 36:        * 還需要擴充套件到根節點,很巧妙
 37:        */
 38:       mySwap(a, 1, len-i+1);
 39:       adjust(a, 1,len-i);
 40:     }
 41:   }
 42: 
 43:   public static void adjust(int[] a, int i, int len) {
 44:     /*
 45:      * 原來犯了一個錯誤,更新上一個節點時沒有更新子節點即沒有采用迴圈結構
 46:      */
 47:     int temp = a[i];
 48:     int j = i<<1;
 49:     /*
 50:      * 為什麼是迴圈?
 51:      * 因為從最後一個非葉子節點開始的,上面的節點更新後可能使下方
 52:      * 已經調整過的節點不再滿足堆的性質,需要繼續調整
 53:      */
 54:     while (j<=len) {
 55:       if (j<len && a[j]>a[j+1]) {
 56:         j++;
 57:       }
 58:       if (temp<=a[j]) {
 59:         break;
 60:       }
 61:       a[j>>1] = a[j];
 62:       //往下擴充套件子節點
 63:       j <<= 1;
 64:     }
 65:     a[j>>1] = temp;
 66:   }
 67: 
 68:   private static void mySwap(int[] a, int i, int j) {
 69:     int temp = a[i];
 70:     a[i] = a[j];
 71:     a[j] = temp;
 72:   }
 73: }
 74: