堆排序(摘自算法導論)
(二叉)堆是一個數組,他可以被看成一個近似的完全二叉樹。樹上的每一個節點對應數組中的一個元素,除了最底層之外,該樹是完全填滿的,而且是從左向右填充。表示堆的數組A包括兩個屬性,A.length給出數組元素的個數,A.heap-size表示有多少個堆元素存儲在該數組中。也就是說,雖然A[1..A.length]可能都有數據,但只有A[1..A.heap-size]中存放的是堆的有效元素。0<=A.heap-size<=A.length。樹的根節點是A[1],這樣給定一個節點的下標i,我們很容易計算得到它的父節點、左孩子和右孩子的下標。
PARENT(i)
return[i/2]
LEFT (i)
return 2i
RIGHT(i)
return 2i+1
二叉堆可以分為兩種形式:最大堆和最小堆。
在最大堆中,最大堆是指除了根節點之外的所有節點i都要滿足
A[PARENT(i)]>=A[i]
也就是說,某個節點的最大值至多與根節點一樣大。因此,堆中最大的元素存在根節點中;並且,在任一子樹中,該子樹所包含的所有節點的值都不大於該子樹根節點的值。最小堆的組織方式正好相反
最小堆性質市值除了根節點之外的所有節點i都有
A[PARENT[i]]<=A[i]
在堆排序算法中,我們使用的是最大堆,最小堆通常用於構造優先隊列。
如果把堆看成一棵樹,我們定義一個堆節點中的節點高度就為該節點到葉子節點最長簡單路徑上的邊數;進而我們可以把堆的高度定義為根節點的高度,既然一個包含n個元素的堆可以看作一個完全二叉樹,那麽該堆的高度是Olgn
堆的幾個過程
- MAX-HEAPIFY 時間復雜度O(lgn),維護最大堆的關鍵
- BUILD-MAX-HEAP 具有線性時間復雜度,功能是從無序的輸入數據數組中構造一個最大堆
- HEAPSORT 時間復雜度O(nlgn) 對一個數組進行原址排序
- MAX-HEAP-INSERT、HEAP-EXTRACT-MAX、HEAP-INCREASE-KEY和HEAP-MAXMUM 時間復雜度為O(lgn),功能是利用堆實現一個優先隊列
思考:
1、在高度為h的堆中,元素個數最多和最少為多少
2、一個一排好序的數組是最小堆嗎
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 = l
else largest = i
if r<=A.heap-size and A[r] > A[largest]
largest = r
if largest != i
swap A[i] A[largest]
MAX-HEAPIFY(A,largest)
建堆
我們可以用自底向上的方法利用過程MAX-HEAPIFY把一個大小為n=A.length的數組A[1..n]轉換為最大堆
BUILD-MAX-HEAP
A.heap-size = A.length
for i = [A.length/2] downto 0
MAX-HEAPIFY (A,i)
堆排序算法
初始時候,堆排序算法利用BUILD-MAX-HEAP將輸入數組A[1...n]建成最大堆。因為數組中最大元素總是在根節點A[1]中,通過把它與A[n]進行互換,我們可以讓該元素放到正確位置,這時候,我們從堆中去掉節點n(這一操作可以通過減少A.heap-size的值來實現),剩余的節點中,原來的孩子節點仍然是最大堆,而新的根節點可能會違背最大堆的特性。為了維護最大堆的特性,我們要做的是調用MAX-HEAFIFY(A,1),從而在A[1..n-1]上構造一個新的最大堆,堆排序算法會不斷重復這一過程,直到堆的大小從n-1降到2
HEAPSORT
BUILD-MAX-HEAP(A)
for i = A.length downto 2
swap A[i] A[1]
A.heap-size = A.heap-size - 1
MAX-HEAPIFY(A,1)
用Java來實現這一過程
public class HeapSort {
public void maxHeapIfy(int[] a, int i, int heapSize) {
int l = 2 * i;
int r = 2 * i + 1;
int largest;
if (l <= heapSize - 1 && a[l] > a[i]) {
largest = l;
} else {
largest = i;
}
if (r <= heapSize - 1 && a[r] > a[largest]) {
largest = r;
}
if (largest != i) {
swap(a, i, largest);
maxHeapIfy(a, largest, heapSize);
}
}
public void swap(int[] a, int i, int j) {
int swap = a[j];
a[j] = a[i];
a[i] = swap;
}
public void buildMaxHeap(int[] a) {
int heapSize = a.length;
for (int i = heapSize / 2; i >= 0; i--) {
maxHeapIfy(a, i, heapSize);
}
}
public void heapSort(int[] a) {
int heapSize = a.length;
buildMaxHeap(a);
for (int i = heapSize - 1; i >= 0; i--) {
swap(a, 0, i);
maxHeapIfy(a, 0, i);
}
}
@Test
public void heapSort() {
int[] a = {1, 4, 2, 3, 9, 7, 8, 10, 14, 16};
heapSort(a);
for (int i : a) {
System.out.print(i + " ");
}
}
}
堆排序(摘自算法導論)