堆排序实现教程

发布时间:2026/7/2 2:44:37
堆排序实现教程
堆排序算法详解从原理到实现一、什么是堆排序堆排序Heap Sort是一种基于二叉堆数据结构的比较排序算法由J.W.J. Williams于1964年发明。它结合了插入排序和归并排序的优点具有原地排序只需要常数级别的额外空间和O(n log n)时间复杂度的特点。堆排序的核心思想是将待排序的序列构造成一个大顶堆或小顶堆此时整个序列的最大值或最小值就是堆顶的根节点。将其与末尾元素交换然后将剩余的n-1个序列重新构造成一个堆如此反复执行便能得到一个有序序列。二、堆的基本概念2.1 二叉堆的定义二叉堆是一种特殊的完全二叉树它满足以下性质- 结构性质是一棵完全二叉树除最后一层外其余层都是满的且最后一层的节点都靠左排列- 堆序性质对于大顶堆每个节点的值都大于或等于其子节点的值对于小顶堆每个节点的值都小于或等于其子节点的值2.2 堆的存储方式由于堆是完全二叉树我们可以使用数组来高效地存储堆结构- 对于索引为i的节点- 父节点索引floor((i-1)/2)- 左子节点索引2i 1- 右子节点索引2i 2这种存储方式避免了指针的使用节省了空间同时保持了高效的访问性能。三、堆排序算法步骤堆排序主要分为两个阶段3.1 建堆阶段Heapify将无序序列构建成一个堆具体步骤如下1. 从最后一个非叶子节点开始索引为n/2-1向前依次对每个节点进行下沉操作2. 下沉操作比较节点与其子节点的值如果不满足堆的性质则与较大的子节点交换位置然后继续向下调整3.2 排序阶段1. 将堆顶元素最大值与最后一个元素交换2. 堆的大小减1对新的堆顶元素进行下沉操作重新调整堆3. 重复上述过程直到堆的大小为1四、堆排序的代码实现以下是堆排序的完整实现以大顶堆为例pythondef heapify(arr, n, i):对以i为根的子树进行堆调整arr: 待调整的数组n: 堆的大小i: 当前节点的索引largest i 初始化最大值为当前节点left 2 i 1 左子节点索引right 2 i 2 右子节点索引如果左子节点存在且大于当前最大值if left n and arr[left] arr[largest]:largest left如果右子节点存在且大于当前最大值if right n and arr[right] arr[largest]:largest right如果最大值不是当前节点交换并继续调整if largest ! i:arr[i], arr[largest] arr[largest], arr[i]heapify(arr, n, largest)def heap_sort(arr):堆排序主函数n len(arr)构建大顶堆从最后一个非叶子节点开始for i in range(n // 2 - 1, -1, -1):heapify(arr, n, i)逐个提取元素for i in range(n - 1, 0, -1):将当前堆顶元素最大值与末尾元素交换arr[0], arr[i] arr[i], arr[0]调整剩余元素为堆heapify(arr, i, 0)return arr测试示例if __name__ __main__:测试用例test_cases [[12, 11, 13, 5, 6, 7],[64, 34, 25, 12, 22, 11, 90],[5, 1, 4, 2, 8],[1],[]]for i, arr in enumerate(test_cases):print(f测试用例 {i1}: 原始数组: {arr})sorted_arr heap_sort(arr.copy())print(f排序后数组: {sorted_arr})print(- —)五、算法复杂度分析5.1 时间复杂度- 建堆阶段O(n)- 看起来应该是O(n log n)但由于完全二叉树的特性实际复杂度为O(n)- 证明对于高度为h的节点最多需要下沉h次而高度为h的节点最多有ceil(n/2^(h1))个- 排序阶段O(n log n)- 每次提取堆顶元素需要O(log n)时间- 需要执行n-1次提取操作- 总时间复杂度O(n log n)- 在所有情况下最好、最坏、平均都是O(n log n)5.2 空间复杂度- 原地排序O(1)- 只需要常数级别的额外空间用于交换元素- 递归实现的空间复杂度为O(log n)递归调用栈但可以改为迭代实现达到O(1)5.3 稳定性堆排序是不稳定的排序算法。在交换堆顶元素和末尾元素时可能会改变相同元素的相对顺序。六、堆排序的优缺点6.1 优点1. 时间复杂度稳定最好、最坏、平均情况下都是O(n log n)2. 空间效率高只需要O(1)的额外空间3. 适用于大数据集当内存有限时堆排序是很好的选择4. 可以获取部分排序结果如果需要前k个最大/最小元素只需要执行k次提取操作6.2 缺点1. 不稳定相同元素的相对顺序可能会改变2. 缓存不友好数组的跳跃访问模式父子节点索引计算导致缓存命中率低3. 常数因子较大实际运行时间通常比快速排序慢七、堆排序的应用场景1. 优先级队列的实现堆是优先级队列的理想数据结构2. Top-K问题查找最大或最小的k个元素3. 实时系统需要保证最坏情况下的性能4. 内存受限环境需要原地排序的场景5. 外部排序堆排序可以用于多路归并八、堆排序的优化与变体8.1 迭代实现递归实现的heapify函数可能造成栈溢出可以改为迭代实现pythondef heapify_iterative(arr, n, i):迭代实现的堆调整while True:largest ileft 2 i 1right 2 i 2if left n and arr[left] arr[largest]:largest leftif right n and arr[right] arr[largest]:largest rightif largest i:breakarr[i], arr[largest] arr[largest], arr[i]i largest8.2 小顶堆实现只需修改比较条件即可实现小顶堆排序pythondef heapify_min(arr, n, i):smallest ileft 2 i 1right 2 i 2if left n and arr[left] arr[smallest]:smallest leftif right n and arr[right] arr[smallest]:smallest rightif smallest ! i:arr[i], arr[smallest] arr[smallest], arr[i]heapify_min(arr, n, smallest)8.3 自底向上的建堆方法Floyd提出的自底向上建堆方法更加高效pythondef build_heap_floyd(arr):n len(arr)从最后一个非叶子节点开始向前遍历for i in range(n // 2 - 1, -1, -1):对每个节点执行下沉操作heapify(arr, n, i)九、堆排序与其他排序算法的比较| 算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 ||------|---------------|----------------|------------|--------|| 堆排序 | O(n log n) | O(n log n) | O(1) | 不稳定 || 快速排序 | O(n log n) | O(n2) | O(log n) | 不稳定 || 归并排序 | O(n log n) | O(n log n) | O(n) | 稳定 || 插入排序 | O(n2) | O(n2) | O(1) | 稳定 |十、学习建议与总结堆排序是一种优雅而高效的排序算法它展示了数据结构与算法的完美结合。学习堆排序不仅是为了掌握一种排序方法更重要的是理解堆这种数据结构的思想和应用。学习建议1. 动手实现亲自编写代码实现堆排序加深理解2. 可视化工具使用排序可视化工具观察堆排序的过程3. 复杂度推导尝试推导堆排序的时间复杂度4. 应用扩展探索堆在优先级队列、Top-K问题中的应用堆排序虽然在实际应用中可能不如快速排序快但其稳定的O(n log n)时间复杂度和O(1)空间复杂度使其在特定场景下具有不可替代的优势。理解堆排序的原理和实现对于提升算法设计和分析能力具有重要意义。通过本文的学习你应该已经掌握了堆排序的核心思想、实现方法和应用场景。接下来可以尝试解决一些基于堆的问题如合并k个有序链表、数据流的中位数等进一步巩固和拓展对堆数据结构的理解。