| 项目 | 值 |
|---|---|
| 主题 | 堆排序 |
| 核心概念 | 基于完全二叉树的选择排序,利用堆结构,是简单选择排序的改进 |
| 时间复杂度 | 建堆 $O(n)$,排序 $O(n\log_2 n)$,总 $O(n\log_2 n)$;最好/平均/最坏均同阶 |
| 空间复杂度 | $O(1)$(原地排序) |
| 稳定性 | ❌ 不稳定 |
| 难度 | ⭐⭐⭐⭐ |
堆排序(Heap Sort)是一种基于完全二叉树的选择排序算法。它利用堆这种数据结构来进行排序,是简单选择排序的改进版本。
基于大根堆的堆排序得到递增序列,基于小根堆得到递减序列。
// 向下调整函数(大根堆)
void SiftDown(int A[], int start, int end) {
int parent = start;
int child = 2 * parent + 1; // 左孩子(数组下标从 0 开始)
while (child <= end) {
// 选择较大的孩子
if (child + 1 <= end && A[child] < A[child + 1])
child++;
// 如果父结点 >= 较大的孩子,调整结束
if (A[parent] >= A[child])
return;
else {
// 交换父子结点
int temp = A[parent];
A[parent] = A[child];
A[child] = temp;
// 继续向下调整
parent = child;
child = 2 * parent + 1;
}
}
}
// 建堆 + 堆排序
void HeapSort(int A[], int len) {
// 第一步:建堆,从最后一个非叶子结点开始
for (int i = (len - 2) / 2; i >= 0; i--)
SiftDown(A, i, len - 1);
// 第二步:排序
for (int i = len - 1; i > 0; i--) {
// 将堆顶(最大值)与堆底交换
int temp = A[0];
A[0] = A[i];
A[i] = temp;
// 对剩余元素重新调整为堆
SiftDown(A, 0, i - 1);
}
}
对序列 {53, 17, 78, 9, 45, 65, 87, 23} 进行堆排序(大根堆)。
[53,17,78,9,45,65,87,23],最后非叶子结点为 4 号(值 9)。[53,17,78,23,45,65,87,9][53,17,87,23,45,65,78,9][53,45,87,23,17,65,78,9][87,45,53,23,17,65,78,9];继续下坠结点3(53):孩子 6(65)、7(78),53 < 78 → 与 7 交换 → [87,45,78,23,17,65,53,9]建堆完成,得到大根堆 [87,45,78,23,17,65,53,9]。
| 趟次 | 交换 | 调整后堆顶 | 已确定最终位置 |
|---|---|---|---|
| 第 1 趟 | 87 ↔ 9 | 78 上浮到堆顶 | 87 |
| 第 2 趟 | 78 ↔ 53 | 65 上升 | 87, 78 |
| 第 3 趟 | 65 ↔ 23 | 45 上升 | 87, 78, 65 |
| … | 依次下坠调整 | … | … |
最终结果:9, 17, 23, 45, 53, 65, 78, 87。
| 情况 | 时间复杂度 | 说明 |
|---|---|---|
| 最好情况 | $O(n\log_2 n)$ | 序列有序时 |
| 平均情况 | $O(n\log_2 n)$ | 随机输入 |
| 最坏情况 | $O(n\log_2 n)$ | 序列逆序时 |
空间复杂度为 $O(1)$
堆排序是不稳定的排序算法。
反例:序列 {1, 1*, 2} 建大根堆。建堆后:{2, 1*, 1},交换 2 和 1 后:{1, 1*, 2},1 和 1* 的相对位置改变了。
{1, 1*, 2} 建大根堆后交换会导致不稳定。(暂无关联知识点)