http://fangjian0423.github.io/2016/04/09/heap-heapsort/
堆的概念:
n個元素序列 { k1, k2, k3, k4, k5, k6 …. kn } 當且僅當滿足以下關系時才會被稱為堆:
ki <= k2i,ki <= k2i+1 或者 ki >= k2i,ki >= k2i+1 (i = 1,2,3,4 .. n/2)
如果數組的下表是從0開始,那么需要滿足
ki <= k2i+1,ki <= k2i+2 或者 ki >= k2i+1,ki >= k2i+2 (i = 0,1,2,3 .. n/2)
比如 { 1,3,5,10,15,9 } 這個序列就滿足 [1 <= 3; 1 <= 5], [3 <= 10; 3 <= 15], [5 <= 9] 這3個條件,這個序列就是一個堆。
所以堆其實是一個序列(數組),如果這個序列滿足上述條件,那么就把這個序列看成堆。
堆的實現通常是通過構造二叉堆,因為二叉堆應用很普遍,當不加限定時,堆通常指的就是二叉堆。
二叉堆的概念:
二叉堆是一種特殊的堆,是一棵完全二叉樹或者是近似完全二叉樹,同時二叉堆還滿足堆的特性:父節點的鍵值總是保持固定的序關系于任何一個子節點的鍵值,且每個節點的左子樹和右子樹都是一個二叉堆。
當父節點的鍵值總是大于或等于任何一個子節點的鍵值時為最大堆。 當父節點的鍵值總是小于或等于任何一個子節點的鍵值時為最小堆。
上圖中的最小堆對應的序列是: [1,3,5,9,10,15] 滿足最小堆的特性(父節點的鍵值小于或等于任何一個子節點的鍵值,并且也滿足堆的性質 [1 <= 3; 1 <= 5], [3 <= 9; 3 <= 10], [5 <= 15])
上圖中的最大堆對應的序列是: [15,10,9,7,5,3] 滿足最大堆的特性(父節點的鍵值大于或等于任何一個子節點的鍵值,并且也滿足堆的性質 [15 >= 10; 15 >= 9], [10 >= 7; 10 >= 5], [9 >= 3])
堆排序指的是對堆這種數據結構進行排序的一種算法。其基本思想如下,以最大堆為例:
將數組序列構建成最大堆[ A1, A2, A3 .. An],這個堆是一個剛初始化無序區,同時有序區為空
堆頂元素A1與最后一個元素An進行交換,得到新的有序區[An],無序區變成[A1 … An-1]
交換之后可能導致[A1 … An-1]這個無序區不是一個最大堆,[A1 … An-1]無序區重新調整成最大堆。重復步驟2,A1與An-1進行交換,得到新的有序區[An,An-1],無序區變成[A1 … An-2].. 不斷重復,直到有序區的個數為n-1才結束排序過程
構造堆的過程如下(以最大堆為例):
從最后一個非葉子節點開始調整,遍歷節點和2個子節點,選擇鍵值最大的節點的鍵值代替父節點的鍵值,如果進行了調整,調整之后的兩個子節點可能不符合堆特性,遞歸調整。一直直到調整完根節點。
以序列[3,5,15,9,10,1]為例進行的堆排序:
首先第1步先把數組轉換成完全二叉樹:
接下來是第2、3步構造有序區和無序區:
構造完之后有序區的元素依次是:1,3,5,9,10,15
簡單地使用java寫一下堆排序:
public class HeapSort {
public static void maxHeapify(int[] arr, int size, int index) {
int leftSonIndex = 2 * index + 1;
int rightSonIndex = 2 * index + 2;
int temp = index;
if(index <= size / 2) {
if(leftSonIndex < size && arr[temp] < arr[leftSonIndex]) {
temp = leftSonIndex;
}
if(rightSonIndex < size && arr[temp] < arr[rightSonIndex]) {
temp = rightSonIndex;
}
// 左右子節點的值存在比父節點的值更大
if(temp != index) {
swap(arr, index, temp); // 交換值
maxHeapify(arr, size, temp); // 遞歸調整
}
}
}
public static void heapSort(int[] arr, int size) {
// 構造成最大堆
buildMaxHeap(arr, arr.length);
for(int i = size - 1; i > 0; i --) {
// 先交換堆頂元素和無序區最后一個元素
swap(arr, 0, i);
// 重新調整無序區
buildMaxHeap(arr, i - 1);
}
}
public static void buildMaxHeap(int[] arr, int size) {
for(int i = size / 2; i >= 0; i --) { // 最后一個非葉子節點開始調整
maxHeapify(arr, size, i);
}
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void main(String[] args) {
int[] arr = { 3, 5, 15, 9, 10, 1};
System.out.println("before build: " + Arrays.toString(arr)); // before build: [3, 5, 15, 9, 10, 1]
buildMaxHeap(arr, arr.length);
System.out.println("after build: " + Arrays.toString(arr)); // after build: [15, 10, 3, 9, 5, 1]
heapSort(arr, arr.length);
System.out.println("after sort: " + Arrays.toString(arr)); // after sort: [1, 3, 5, 9, 10, 15]
}
}
添加
在最大堆[ 15,10,9,7,5,3 ]上添加一個新的元素 11 ,執行的步驟如下:
刪除
在最大堆[ 15,10,9,7,5,3 ]上刪除元素 10 ,執行的步驟如下: