1. 程式人生 > >堆排序--java

堆排序--java

堆是一種完全二叉樹,每個結點的值都大於或等於其左右孩子結點的值,稱為大頂堆;或者每個結點的值都小於或等於其左右孩子結點的值,稱為小頂堆。
先引入一下完全二叉樹的概念。

完全二叉樹
定義:完全二叉樹是由滿二叉樹而引出來的。對於深度為K的,有n個結點的二叉樹,當且僅當其每一個結點都與深度為K的滿二叉樹中編號從1至n的結點一一對應時稱之為完全二叉樹。
具有性質:
1) 深度為k的完全二叉樹,至少有2^(k-1)個節點,至多有2^k-1個節點。
2)樹高h=log2n + 1。

滿二叉樹
一棵深度為k,且有2^k-1個節點的樹是滿二叉樹。
另一種定義:除了葉結點外每一個結點都有左右子葉且葉子結點都處在	最底層的二叉樹。
這兩種定義是等價的
性質:
1) 如果一顆樹深度為h,最大層數為k,且深度與最大層數相同,即k=h;
2)它的葉子數是: 2^(h-1)
3)  第k層的結點數是: 2^(k-1)
4) 總結點數是: 2^k-1 (2的k次方減一)
5)總節點數一定是奇數。
6)樹高:h=log2(n+1)。

通俗的講:滿二叉樹,就是每一個父節點都有兩個葉子節點,每一層要不全是葉子節點,要不全是父節點(也即是說每一層都是滿節點的)。二完全二叉樹則是除了最後一層外,其他的層都是滿的,最後一層的葉子節點可以不滿,且最後一層的葉子節點從左到右,左邊沒滿,右邊不能有子節點(最後一層的葉子節點都靠左排列)。

堆排序(Heapsort)是指利用堆這種資料結構所設計的一種排序演算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。堆排序可以說是一種利用堆的概念來排序的選擇排序。分為兩種方法:大頂堆的生序排序,小頂堆的降序排序。
程式碼實現:

import java.util.Arrays;
public class HeapSort {
	public int[] sort(int[] sourceArray) throws Exception {
        // 對 arr 進行拷貝,不改變引數內容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
        int len = arr.length;
        buildMaxHeap(arr, len);//構建大頂堆
        for (int i = len - 1; i > 0; i--) {
            swap(arr, 0, i);//使得陣列的最後一定為最大的數
            len--;
            heapify(arr, 0, len);//重新整理大頂堆
        }
        return arr;
    }
	/**
	 * 構建大頂堆
	 * @param arr 資料陣列
	 * @param len 陣列長度
	 */
    private void buildMaxHeap(int[] arr, int len) {
    	//(int) Math.floor(len / 2) 向下取整,但是Math.floor()返回的型別是double,所以要強轉成int
        for (int i = (int) Math.floor(len / 2); i >= 0; i--) {
            heapify(arr, i, len);
        }
    }
    
    /**
     * 使得父節點大於等於左右子節點
     * @param arr 陣列
     * @param i  父節點位置
     * @param len 陣列長度
     */
    private void heapify(int[] arr, int i, int len) {
        int left = 2 * i + 1; //左子節點位置
        int right = 2 * i + 2;//右子節點位置
        int largest = i;

        if (left < len && arr[left] > arr[largest]) {
            largest = left;//如果左子節點的值大於父節點位置的值,則替換largest
        }

        if (right < len && arr[right] > arr[largest]) {
            largest = right;//如果右子節點的值大於父節點位置的值,則替換largest
        }

        if (largest != i) {
            swap(arr, i, largest);//交換使得父節點的值比左右子節點的值都大。
            heapify(arr, largest, len);
        }
    }
  
/**
 * 交換函式
 */
    private void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

}

程式碼都寫有註釋,看懂應該不難,但是讓自己手寫一個,還真是得思路比較清晰才行,heapify()方法看著很簡單,確實很關鍵。細細品味。

測試main函式:


public class MainTest {
public static void main(String[] args) throws Exception {
	HeapSort heap=new HeapSort();
        int[] array={5,2,7,3,6,1,4};
        array=heap.sort(array);
        for (int i=0;i<array.length;i++){
            System.out.print(array[i]+",");
        }
	}
}

結果:

1,2,3,4,5,6,7,