算法學習與代碼實現5——堆排序
堆排序的思想當年看《算法導論》看了好幾遍也沒理解,現在算是明白了。
算法思路
堆排序藉助的是堆這個數據結構。堆首先是個完全二叉樹,可以用數組表示。下面一張《算法導論》中的插圖形象的表示了二叉堆在數組中的表示:
而最大堆又滿足一個額外的條件:
A[PARENT(i)] ≥A[i]
這樣,在堆排序中,我們需要首先將一個數組變成最大堆,然後將數組的第一個數和最後一個數互換,再將最後一個數從堆中排除,剩下的數再次進行這個過程,知道所有的數都從數組中派出,被排除的數就組成了一個排好序的序列。
算法性能
穩定性:不穩定
時間複雜度: O(nlogn)
空間複雜度:O(1)
僞代碼
堆排序有兩個輔助函數,一個是保持堆的性質,一個是建立最大堆。
保持堆的性質
MAX-HEAPIFY(A, i)
l <- LEFT(i)
r <- RIGHT(i)
if l ≤ heap-size[A] and A[l] > A[i]
then largest <- l
else largest <- i
if r ≤ heap-size[A] and A[r] > A[largest]
then largest <- r
if largest ≠ i
then exchange A[i] <-> A[largest]
MAX-HEAPIFY(A, largest)
建立最大堆
BUILD-MAX-HEAP(A)
heap-size[A] <- length[A]
for i <- length[A]/2向下取整 downto 1
do MAX-HEAPIFY(A, i)
堆排序
HEAPSORT(A)
BUILD-MAX-HEAP(A)
for i <- length[A] downto 2
do exchange A[1] <->A[i]
heap-size[A] <- heap-size[A] - 1
MAX-HEAPIFY(A, 1)
C語言實現
堆排序對數組序號的使用比較多,因爲它完全就是使用數組的需要來表示數組中的元素在堆中的位置。相互關係也要用到序號。而《算法導論》中的介紹都是從序號1開始的,c語言實現中爲了避免不必要的麻煩,也從序號1開始。
保持堆的性質
#define LEFT(i) (2 * i)
#define RIGHT(i) (2 * i + 1)
void max_heapify(int * array, int node, int array_size){
int heap_size = array_size;
int l = LEFT(node);
int r = RIGHT(node);
int largest;
if (l <= heap_size && array[l] > array[node]) {
largest = l;
}
else {
largest = node;
}
if (r <= heap_size && array[r] > array[largest]) {
largest = r;
}
if (largest != node) {
int tmp = array[node];
array[node] = array[largest];
array[largest] = tmp;
}
if (largest != node)
max_heapify(array, largest, array_size);
}
建立最大堆
void build_max_heap(int * array, int array_size){
for (int i = array_size/2; i >= 1; i--) {
max_heapify(array, i, array_size);
}
}
堆排序
void heap_sort(int * array, int array_size){
build_max_heap(array, array_size);
int heap_size = array_size;
for (int i = array_size; i >= 2; i--) {
int tmp = array[1];
array[1] = array[i];
array[i] = tmp;
heap_size -= 1;
max_heapify(array, 1, heap_size);
}
}