堆排序算法


一、堆排序算法的基本特性
時間復雜度:O(n*lgn)
最壞:O(n*lgn)
空間復雜度:O(1)
不穩定。

堆排序是一種選擇排序算法,與關鍵字的初始排列次序無關,即就是在最好,最壞,一般的情況下排序時間復雜度不變。對包含n個數的輸入數組,平均時間為O(nlgn),最壞情況(已經排好序)也是是O(nlgn),最好情況(完全無序)也是O(nlgn)。由於不但時間復雜度少,而且空間復雜度也是最少的,所以是用於排序的最佳選擇。因為,基於比較的排序,最快也只能達到O(nlgn),雖然快排也可以達到這個這個水平,但是快排的時間復雜度是跟關鍵字的初始排序有關,最壞的情況退化成O(n^2),而且快排的空間復雜度是O(n*lgn)。

二、堆的定義

n個元素的序列{k1,k2,…,kn}當且僅當滿足下列關系之一時,稱之為堆。

  情形1:k<= k2i 且k<= k2i+1 最小化堆小頂堆:左、右子孩子的值比父結點的值都大

  情形2:k>= k2i 且k>= k2i+1 (化堆大頂堆:左、右子孩子的值比父結點的值都小

  其中i=1,2,…,n/2向下取整;

若將和此序列對應的一維數組(即以一維數組作此序列的存儲結構)看成是一個完全二叉樹,則堆的含義表明,完全二叉樹中所有非葉子結點的值均不大於(或不小於)其左、右孩子結點的值。

  由此,若序列{k1,k2,…,kn}是堆,則堆頂元素(或完全二叉樹的根)必為序列中n個元素的最小值(或最大值)。

  例如,下列兩個序列為堆,對應的完全二叉樹如圖:

若在輸出堆頂的最小值之后,使得剩余n-1個元素的序列重又建成一個堆,則得到n個元素的次小值。如此反復執行,便能得到一個有序序列,這個過程稱之為堆排序

  堆排序(Heap Sort)只需要一個記錄元素大小的輔助空間(供交換用),每個待排序的記錄僅占有一個存儲空間。

三、堆的存儲

一般用數組來表示堆,若根結點存在序號0處, i結點的父結點下標就為(i-1)/2。i結點的左右子結點下標分別為2*i+12*i+2

  (注:如果根結點是從1開始,則左右孩子結點分別是2i和2i+1。)

  如第0個結點左右子結點下標分別為1和2。

  如大頂堆如下:

左圖為其存儲結構,右圖為其邏輯結構。

四、堆排序的實現

實現堆排序需要解決兩個問題:

    1.如何由一個無序序列建成一個堆?

    2.如何在輸出堆頂元素之后,調整剩余元素成為一個新的堆?

   先考慮第二個問題,一般在輸出堆頂元素之后,視為將這個元素排除,然后用表中最后一個元素填補它的位置,自上向下進行調整:首先將堆頂元素和它的左右子樹的根結點進行比較,把最小的元素交換到堆頂;然后順着被破壞的路徑一路調整下去,直至葉子結點,就得到新的堆。

  我們稱這個自堆頂至葉子的調整過程為“篩選”

  從無序序列建立堆的過程就是一個反復“篩選”的過程。

構造初始堆

  初始化堆的時候是對所有的非葉子結點進行篩選。

  假設有n個元素的堆,那么最后一個非葉子元素的下標是[n/2]-1(向下取整),所以篩選只需要從第[n/2]-1個元素開始,從后往前進行調整。

  比如,給定一個數組,首先根據該數組元素構造一個完全二叉樹。

  然后從最后一個非葉子結點開始,每次都是從父結點、左孩子、右孩子中進行比較交換,交換可能會引起孩子結點不滿足堆的性質,所以每次交換之后需要重新對被交換的孩子結點進行調整。

進行堆排序

  有了初始堆之后就可以進行排序了。

  堆排序是一種選擇排序。建立的初始堆為初始的無序區。

  排序開始,首先輸出堆頂元素(因為它是最值),將堆頂元素和最后一個元素交換,這樣,第n-1個位置(即最后一個位置)作為有序區,前n-2個位置仍是無序區,對無序區進行調整,得到堆之后,再交換堆頂和最后一個元素,這樣有序區長度變為2。。。

  不斷進行此操作,將剩下的元素重新調整為堆,然后輸出堆頂元素到有序區。每次交換都導致無序區-1,有序區+1。不斷重復此過程直到有序區長度增長為n-1,排序完成。

堆排序實例

   首先,建立初始的堆結構如圖:

然后,交換堆頂的元素和最后一個元素,此時最后一個位置作為有序區(有序區顯示為黃色),然后進行其他無序區的堆調整,重新得到大頂堆后,交換堆頂和倒數第二個元素的位置……

重復此過程:

最后,有序區拓展完成,即排序完成:

由排序過程可見,若想得到升序,則建立大頂堆,若想得到降序,則建立小頂堆

五、代碼

假設排列的元素為整型,且元素的關鍵字為其本身。

因為要進行升序排列,所以用大頂堆。

根結點從0開始,所以i結點的左右孩子結點的下標為2i+1和2i+2。

import java.util.Scanner;

/**
 * 堆排序
 * 時間復雜度:O(n*lgn)
 * 空間復雜度:O(1)
 * @author breeze
 *
 */
public class HeapSort {
    /**
     * 堆排序的主方法
     * 
     * @param array
     * @return
     */
    public static int[] sortHeap(int[] array) {
        buildHeap(array);// 構建堆
        int n = array.length;//數組中元素的個數
        
        for (int i = n - 1; i >= 1; i--) {
            swap(array, 0, i);//交換首尾元素
            adjustHeap(array, 0, i);//調整堆
        }
        return array;
    }


    /**
     * 構建大頂堆堆
     * 從最后一個非葉子結點開始調整堆。每次都是從父結點,左節點,右結點三者中選最大值與父結點交換
     * @param array
     */
    private static void buildHeap(int[] array) {
        int n = array.length;// 數組中元素的個數
        
        for (int i = n / 2 - 1; i >= 0; i--)//i= n/2-1  表示最后一個非葉子結點的索引值
            adjustHeap(array, i, n);
    }
    /**
     * 調整堆
     * @param A
     * @param idx
     * @param max 堆的大小
     */
    private static void adjustHeap(int[] A, int idx, int max) {
        int left = 2 * idx + 1;// 左孩子的下標(如果存在的話)
        int right = left + 1;// 左孩子的下標(如果存在的話)
        int largest = 0;// 尋找3個節點中最大值節點的下標
        
        if (left < max && A[left] > A[idx])//左孩子的值比父結點的值大
            largest = left;
        else
            largest = idx;
        
        if (right < max && A[right] > A[largest])
            largest = right;
        
        if (largest != idx) {
            swap(A, largest, idx);//從左節點,右結點中選最大值與父結點交換
            adjustHeap(A, largest, max);
        }
    }
    private static void swap(int[] array, int i, int i2) {
        int temp = array[i];
        array[i] = array[i2];
        array[i2] = temp;
    }
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in); 
          while(sc.hasNextLine()){ 
            String str = sc.nextLine(); //獲取一行 
            String[] words = str.trim().split(" ");//把一行的數據,分割為單個的數據的數組  
            int length = words.length; 
            //字符串還需要轉為int,或者其他的類型
            int[] params = new int[length];
            for (int i = 0;i<length;i++){ 
                 if ("".equals(words[i]) || "\n".equals(words[i])) continue;  
                 params[i] = Integer.parseInt(words[i]);
            }
            //調用需要測試的函數
            int[] array = sortHeap(params);
            //打印結果
            for(int i : array){
                System.out.print(i+" ");
            }
          } 
        
    }

}

 圖片和內容參考:http://www.cnblogs.com/mengdd/archive/2012/11/30/2796845.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM