5-4 其他排序:堆排序
堆排序
堆排序是一种基于二叉堆数据结构的比较排序算法。
- 它是选择排序算法的优化版本。
- 该算法反复查找最大(或最小)元素,并将其与最后一个(或第一个)元素交换。
- 使用二叉堆可以在 O(log n) 时间内高效访问最大(或最小)元素,而不是 O(n)。
- 对剩余元素重复此过程,直到数组排序完成。
- 总体而言,堆排序的时间复杂度为 O(n log n)。
堆排序算法:
首先使用堆化(heapify)将数组转换为最大堆。请注意,此操作是原地进行的。数组元素会重新排列以符合堆的属性。然后,逐个删除最大堆的根节点,并将其替换为最后一个节点,然后再次进行堆化。重复此过程,直到堆的大小大于 1。
堆排序的详细工作原理
步骤 1:将数组视为完全二叉树
我们首先需要将数组可视化为一棵完全二叉树。对于大小为 n 的数组,根节点位于索引 0,索引为 i 的元素的左子节点位于 2i + 1,右子节点位于 2i + 2。

步骤 2:构建最大堆







步骤 3:对数组进行排序,将最大元素放在未排序数组的末尾。






上图展示了对数组进行排序的一些步骤。我们需要重复这些步骤,直到堆中只剩下一个元素为止。
代码实现
#include <iostream>
#include <vector>
// To heapify a subtree rooted with node i
void heapify(std::vector<int>& arr, int n, int i)
{
// Initialize largest as root
int largest = i;
// left index = 2*i + 1
int l = 2 * i + 1;
// right index = 2*i + 2
int r = 2 * i + 2;
// If left child is larger than root
if (l < n && arr[l] > arr[largest])
{
largest = l;
}
// If right child is larger than largest so far
if (r < n && arr[r] > arr[largest])
{
largest = r;
}
// If largest is not root
if (largest != i)
{
std::swap(arr[i], arr[largest]);
// Recursively heapify the affected sub-tree
heapify(arr, n, largest);
}
}
// Main function to do heap sort
void heapSort(std::vector<int>& arr)
{
int n = arr.size();
// Build heap (rearrange vector)
for (int i = n / 2 - 1; i >= 0; i--)
{
heapify(arr, n, i);
}
// One by one extract an element from heap
for (int i = n - 1; i > 0; i--)
{
// Move current root to end
std::swap(arr[0], arr[i]);
// Call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
int main()
{
std::vector<int> arr = { 9, 4, 3, 8, 10, 2, 5 };
heapSort(arr);
for (int i = 0; i < arr.size(); ++i)
{
std::cout << arr[i] << " ";
}
return 0;
}
输出:

堆排序的复杂度、要点、以及优缺点:
堆排序的复杂度:
时间复杂度: O(n log n);
辅助空间: O(log n),这是由于递归调用栈造成的。但是,对于迭代实现,辅助空间可以达到 O(1)。
关于堆排序的要点
- 原地算法。
- 它的典型实现方式并不稳定,但可以通过一些方法使其稳定(参见此处)。
- 通常比实现良好的快速排序慢 2-3 倍。速度慢的原因是缺乏局部性引用。
堆排序的优点
- 高效的时间复杂度:堆排序在任何情况下都保证时间复杂度为 O(n log n),使其适用于大型数据集。log n 因子来源于二叉堆的高度,从而保证了性能的稳定性。
- 内存占用极低:堆排序可以原地执行,仅需极少的额外内存。使用迭代式的 heapify() 函数可以避免占用额外的栈空间,因此除了存储数组本身之外,无需额外的内存。
- 简单性:与其他高效排序算法相比,堆排序相对容易理解和实现,因为它依赖于简单的二叉堆结构,而没有递归等高级概念(如果使用迭代堆化)。
堆排序的缺点
- 成本高昂:堆排序的成本较高,因为即使堆排序和归并排序的时间复杂度均为 O(n log n),堆排序的常数项也比归并排序高。
- 不稳定:堆排序不稳定,可能会改变元素的相对顺序。
- 效率低下:堆排序效率不高,因为其时间复杂度中存在较大的常数。

浙公网安备 33010602011771号