Python实现堆排序案例详解

拾光
拾光
发布时间:2026-10-14| 6人浏览过
原创

本文详细解析Python实现堆排序的原理与步骤。从堆的定义、大顶堆与小顶堆的区别入手,通过具体列表示例演示构建完全二叉树、调整节点及交换堆顶与堆尾的过程。最后提供完整的Python代码实现,并分析堆排序的时间复杂度O(nlogn)及不稳定性。

Python实现堆排序

一、堆排序简介

堆排序(Heap Sort)是一种基于堆这种数据结构的排序算法。

堆在结构上表现为一棵完全二叉树,并满足堆的性质:除了叶节点外,每个节点的值都大于等于(或小于等于)其子节点的值。

关于二叉树和完全二叉树的详细介绍,可参考:https://www.jb51.net/article/222487.htm

堆排序首先将待排序列表中的元素按从上到下、从左到右的顺序构建为一棵完全二叉树,随后调整该树以满足堆的性质。构建完成后,将堆顶元素与堆尾元素交换,并将堆尾元素移出堆(即取出当前最大或最小值)。重复构建堆、交换堆顶与堆尾、取出堆尾元素的操作,直至所有元素被取出,列表即完成排序。

堆结构主要分为大顶堆和小顶堆两种类型:

1. 大顶堆:除叶节点外,每个节点的值均大于等于其子节点的值,根节点为所有节点中的最大值。在堆排序中,大顶堆用于实现升序排列。

2. 小顶堆:除叶节点外,每个节点的值均小于等于其子节点的值,根节点为所有节点中的最小值。在堆排序中,小顶堆用于实现降序排列。

二、堆排序原理

堆排序的具体原理如下:

1. 将待排序列表中的数据按照从上到下、从左到右的顺序,构建为一棵完全二叉树。

2. 对完全二叉树中每个非叶节点,将其值与其子节点(子节点可能有一个或两个)中较大的值进行比较。若节点值小于子节点值,则交换两者的位置(大顶堆情形;小顶堆则反之)。

3. 节点与子节点交换后,需继续将该节点(现位于子节点位置)与孙节点进行比较,直到无需交换或子节点为叶节点时停止。遍历所有非叶节点后,即可构建出完整的堆结构。

4. 数据构建为堆结构后,将堆顶元素与堆尾元素交换,随后将堆尾元素从堆中移除并加入已排序序列。至此完成一轮堆排序,堆中剩余数据个数减1。

5. 重复执行步骤2、3、4,直到堆中所有数据被取出,列表排序完成。

以列表 [10, 17, 50, 7, 30, 24, 27, 45, 15, 5, 36, 21] 进行升序排列为例。列表的初始状态如下图所示。

由于需要进行升序排序,在构造堆结构时应使用大顶堆。

1. 将待排序列表中的数据按从上到下、从左到右的顺序构建为一棵完全二叉树。

2. 从完全二叉树的最后一个非叶节点开始,将其值与子节点中较大的值进行比较,若节点值小于子节点值则交换。本例中24是最后一个非叶子节点,它仅有一个子节点21,因24大于21,故无需交换。

3. 继续处理倒数第二个非叶节点,将其值与子节点中较大的值进行比较,若小于则交换。节点30有两个子节点5和36,其中较大值为36,因30小于36,故交换两者位置。

4. 继续对下一个节点进行比较。因7小于45,故交换两者位置。

5. 继续重复上述过程。因50大于27,无需交换位置。若无需交换,则无需再比较子节点与孙节点。

6. 继续重复,因17小于45,故交换两者位置。

7. 17与45交换位置后,17移动到了子节点位置,此时需继续将其与孙节点进行比较。因17大于15,故无需交换。

8. 继续对下一个节点进行比较,因10小于50,故交换两者位置。

9. 10与50交换位置后,10移动到了子节点位置,需继续将其与孙节点进行比较。因10小于27,故交换两者位置。

10. 此时,一个大顶堆构造完成,满足堆的性质:除叶节点外,每个节点的值均大于等于其子节点的值。

11. 大顶堆构建完成后,将堆顶与堆尾交换位置,随后将堆尾元素从堆中取出。具体操作为将50和21交换位置,交换后21位于堆顶,50(最大数据)位于堆尾,随后将50从堆中取出。

12. 将50从堆中取出后,即找到了待排序列表中的最大值。将50添加到已排序序列中,第一轮堆排序完成,堆中剩余元素个数减1。

13. 取出最大数据后,重复执行将完全二叉树构建成大顶堆、交换堆顶和堆尾、取出堆尾元素的操作。每次操作均取出当前堆中最大的数据并添加到已排序序列中,直到堆中所有数据被取出。

14. 循环执行 n 轮堆排序之后,列表排序完成。排序结果如下图所示。

三、Python实现堆排序


# coding=utf-8
def heap_sort(array):
    first = len(array) // 2 - 1
    for start in range(first, -1, -1):
        # 从下到上,从右到左对每个非叶节点进行调整,循环构建成大顶堆
        big_heap(array, start, len(array) - 1)
    for end in range(len(array) - 1, 0, -1):
        # 交换堆顶和堆尾的数据
        array[0], array[end] = array[end], array[0]
        # 重新调整完全二叉树,构造成大顶堆
        big_heap(array, 0, end - 1)
    return array
 
 
def big_heap(array, start, end):
    root = start
    # 左孩子的索引
    child = root * 2 + 1
    while child <= end:
        # 节点有右子节点,并且右子节点的值大于左子节点,则将child变为右子节点的索引
        if child + 1 <= end and array[child] < array[child + 1]:
            child += 1
        if array[root] < array[child]:
            # 交换节点与子节点中较大者的值
            array[root], array[child] = array[child], array[root]
            # 交换值后,如果存在孙节点,则将root设置为子节点,继续与孙节点进行比较
            root = child
            child = root * 2 + 1
        else:
            break
 
 
if __name__ == '__main__':
    array = [10, 17, 50, 7, 30, 24, 27, 45, 15, 5, 36, 21]
    print(heap_sort(array))

运行结果:

[5, 7, 10, 15, 17, 21, 24, 27, 30, 36, 45, 50]

代码中,首先实现一个 big_heap(array, start, end) 函数,用于比较节点与其子节点中的较大者,若节点值小于子节点值则进行交换。代码中无需真正将数据添加到完全二叉树结构中,而是依据待排序列表中的索引来确定节点与子节点的位置关系。在完全二叉树中,若节点索引为 i,则其左子节点索引为 2*i+1,右子节点索引为 2*i+2。对于包含 n 个节点的完全二叉树,非叶子节点共有 n//2 个。由于列表索引从0开始,因此索引范围为 0 到 n//2-1 的数据对应非叶子节点。

在实现堆排序函数 heap_sort(array) 时,首先调用 big_heap(array, start, end) 函数循环调整所有非叶子节点,以构造大顶堆。随后将堆顶与堆尾交换,将堆尾元素从堆中取出并加入已排序序列,完成一轮堆排序。接着循环构建大顶堆,每次取出最大元素,直到堆中所有数据被取出。

四、堆排序的时间复杂度和稳定性

1. 时间复杂度

在堆排序中,构建一次大顶堆可取出一个元素,完成一轮堆排序,共需进行 n 轮堆排序。每次构建大顶堆时,所需的比较和交换次数平均为 logn(第一轮构建堆时步骤较多,后续重建堆时步骤会大幅减少)。时间复杂度为 T(n)=nlogn,再乘以每次操作的步骤数(常数,不影响大O记法),因此堆排序的时间复杂度为 O(nlogn)。

2. 稳定性

在堆排序过程中,会交换节点与子节点。若存在相等的数据,交换操作可能会改变这些相等数据的相对次序。因此,堆排序是一种不稳定的排序算法。

到此这篇关于Python实现堆排序案例详解的文章就介绍到这了,更多相关Python实现堆排序内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。