堆排序
堆排序是另一种时间复杂度为O(nlogn)的排序算法,它的实现依赖于二叉堆,优点是就地排序,无需额外空间,最坏时间复杂度也是O(nlogn),缺点是不稳定。
理解了二叉堆,堆排序可谓水到渠成。仍以整型数组按从小到大排序为例,由于要求从小到大排,得用大根堆,步骤如下:
- 建立大根堆
- 从堆中弹出最大元素,将它与下标最大的乱序元素交换
- 因弹出堆顶元素对堆做调整
- 反复执行2~3步骤,直到堆空为止
void SiftDown(int *a, int size, int idx) {
int i, j;
for (i = idx; (j = 2 * i + 1) < size; i = j) {
if (j + 1 < size && a[j+1] > a[j])
j += 1;
if (a[j] <= a[i])
break;
swap(&a[i], &a[j]);
}
}
void Heapify(int *a, int size) {
int i;
for (i = (size - 1) / 2; i >= 0; i--)
SiftDown(a, size, i);
}
void HeapSort(int *a, int size) {
int i;
Heapify(a, size);
for (i = size - 1; i > 0; i--) {
swap(&a[0], &a[i]);
SiftDown(a, i, 0);
}
}
由于这里数组下标是从0开始的,需注意:
- 根节点下标为0
- 左子节点下标为2k+1,右子节点下标为2k+2
- 有子节点的最大下标为(size-1)/2