算法学习与代码实现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);
}
}