《算法導論》中堆排序主要將其分爲堆的性質、維護堆的性質、建堆、堆排序算法java
堆的性質:給定一個結點的下標i,很容易計算獲得它的父結點、左孩子和右孩子的下標(僞代碼):算法
PARENT(i) return i/2 LEFT(i) return 2i RIGHT(i) return 2i+1
這裏針對下標從1開始的數組,然而實際上咱們涉及的數組都是從0開始。爲了改進上面的僞代碼,可使用移位來解決,其僞代碼:api
PARENT(i) return (i-1)>>1 LEFT(i) return ((i+1)<<1)-1 RIGHT(i) return (i+1)<<1
維護堆的性質(MAX-HEAPIFY):數組
MAX-HEAPIFY是用於維護最大堆性質的重要過程。他輸入爲一個數據A和一個下標i,在調用MAX-HEAPIFY的時候,假定根節點爲LEFT(i)和RIGHT(i)的二叉樹都是最大堆,但這時A[i]有可能小於其孩子,這樣就違背了最大堆的性質。MAX-HEAPIFY經過讓A[i]的值在最大堆中「逐級降低」,從而使得如下標i爲根結點的子樹從新遵循最大堆的性質。其僞代碼以下所示:測試
MAX-HEAPIFY(A, i) l = LEFT(i); r = RIGHT(i); if l <= A.heap-size and A[l] > A[i] largest = largest else largest = i; if r <= A.heap-size and A[r] > A[largest] largest = r if largest != i exchangeA[i]withA[largest] MAX-HEAPIFY(A, largest)
用Java語言實現維護堆的性質:MAXHeapify.javaui
package heapsort; public class MaxHeapify { public void heapAdjust(int[] A, int i, int len){ int l = Left(i); int r = Right(i); int largest = i;//假設父節點值最大 if (l < len && A[l] > A[i]) {//左孩子值大於父節點值 largest = l; } if (r < len && A[r] > A[largest]) {//右孩子值大於父節點值 largest = r; } if (largest != i) { //exchange A[i]withA[largest] int tmp = A[i] ; A[i] = A[largest]; A[largest] = tmp; heapAdjust(A, largest, len); } } private int Right(int i) {//右孩子座標 return ((i+1)<<1); // return 2*i+1; } private int Left(int i) {//左孩子座標 return ((i+1)<<1)-1; // return 2*i; } }
測試維護堆的性質代碼即:MaxHeapifyTest.javaspa
package heapsort; public class MaxHeapifyTest { public static void main(String[] args) { int[] A ={16, 4, 10, 14, 7, 9, 3, 2, 8, 1}; // int[] A={5, 2, 4, 6, 1, 3, 2, 6}; MaxHeapify maxHeapify = new MaxHeapify(); maxHeapify.heapAdjust(A, 1, A.length); for (int i = 0; i < A.length; i++) { System.out.print(A[i]+" "); } } }
測試結果:code
16 14 10 8 7 9 3 2 4 1
建堆(BUILD-MAX-HEAPIFY):blog
用自底向上的方法利用過程MAX-HEAPIFY把一個大小爲n=A.length的數組A[1..n]轉換爲最大堆。經過一個證實:當用數組表示存儲n個元素的堆時,葉節點下標分別爲floor(n/2)+1,floor(n/2)+2,...,n。可知道,子數組A[floor(n/2)+1..n]中的元素都是樹的葉結點。每一個葉結點均可以當作只包含一個元素的堆。過程BUILD-MAX-HEAPIFY對樹中的其餘結點都調用一次MAX-HEAPIFY。其僞代碼以下:排序
BUILD-MAX-HEAP(A) A.heap-size = A.length for i = floor(A.length/2) downto 1 MAX-HEAPIFY(A, i)
用Java語言實現BUILD-MAX-HEAPIFY功能即:BuildHeap.java
package heapsort; public class BuildHeap { public void buildMaxHeap(int[] A) { for (int i = A.length/2-1; i >= 0; i--) { MaxHeapify maxHeapify = new MaxHeapify(); maxHeapify.heapAdjust(A, i, A.length); } } }
測試建堆的功能即BuildHeapTest.java:
package heapsort; public class BuildHeapTest { public static void main(String[] args) { int[] A = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7}; BuildHeap buildHeap = new BuildHeap(); buildHeap.buildMaxHeap(A); for (int i = 0; i < A.length; i++) { System.out.print(A[i]+" "); } } }
測試結果:
16 14 10 8 7 9 3 2 4 1
堆排序算法:
初始時候,堆排序算法利用BUILD-MAX-HEAP將輸入數組A[1..n]建成最大堆,其中n=A.length。由於數組中的最大元素總在根結點A[1]中,經過把它與A[n]進行互換,咱們可讓該元素放到正確的位置。這時候,若是咱們從堆中去掉結點n(這一操做能夠經過減小A.heap-size的值來實現),剩餘的結點中,原來根的孩子結點仍然是最大堆,而新的根結點可能會違背最大隊的性質。爲了維護最大堆的性質,咱們要作的是調用MAX-HEAPIFY(A, 1),從而在A[1..n-1]上構造一個新的最大堆。堆排序算法會不斷重複這一過程,直到堆的大小從n-1降到2.
HEAPSORT(A) BUILD-MAX-HEAP(A) for i = A.length downto 2 exchange A[1] with A[i] A.heapsize = A.heapsize-1 MAX-HEAPIFY(A, 1)
注意:本段僞代碼數組下標從1開始,要想實現該段僞代碼時候,要將下標改成0開始。
用Java代碼實現堆排序即HeapSort.java:
package heapsort; public class HeapSort { public HeapSort(int[] A) { BuildHeap buildHeap = new BuildHeap(); buildHeap.buildMaxHeap(A); for (int i = A.length-1; i > 0; i--) { //exchange A[1]withA[i] int tmp = A[0]; A[0] = A[i]; A[i] = tmp; //維護堆的性質 MaxHeapify maxHeapify = new MaxHeapify(); maxHeapify.heapAdjust(A, 0, i);//A[0]爲根結點 } } }
測試堆排序算法即HeapSortTest.java:
package heapsort; public class HeapSortTest { public static void main(String[] args) { int[] A = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7}; HeapSort heapSort = new HeapSort(A); for (int j = 0; j < A.length; j++) { System.out.print(A[j]+" "); } } }
測試結果:
1 2 3 4 7 8 9 10 14 16