大数跨境
0
0

【Python】heapq:Python堆排序神器

【Python】heapq:Python堆排序神器 我爱数据科学
2025-10-19
3
导读:大家好呀~今天咱们来聊聊 Python 里超实用的堆排序工具 ——heapq模块。

大家好呀~今天咱们来聊聊 Python 里超实用的堆排序工具 ——heapq模块。不管你是刚入门的编程小白,还是需要优化代码效率的开发者,掌握heapq都能帮你在数据处理上省不少事,快跟着文章一起学起来吧!

一、heapq 模块:到底是干嘛的?

heapq的核心作用,就是实现最小堆排序算法,而且能直接和 Python 的列表(list)配合使用,特别方便。

首先得明确一个概念:什么是 “堆”?

堆是一种类似树的数据结构,其中子节点和父节点之间存在固定的排序关系。咱们常用的 “二叉堆”,可以用列表(或数组)来表示 —— 如果元素的索引是 N(从 0 开始计数),那它的两个子节点索引就分别是2*N+12*N+2

这种结构的好处很明显:堆的调整可以 “原地” 进行,添加或删除元素时不用重新分配大量内存,效率超高!

二、两种堆:最大堆 vs 最小堆

堆主要分两种,咱们得先搞清楚区别:

最大堆(max-heap):父节点的值 ≥ 两个子节点的值;

最小堆(min-heap):父节点的值 ≤ 两个子节点的值。

划重点!Python 的heapq模块实现的是最小堆,这一点在后续实战中会频繁用到~

三、实战准备:示例数据与辅助工具

接下来的例子,都会用到两份基础文件,先给大家展示一下:

1. 示例数据文件:heapq_heapdata.py

这份文件里是咱们要处理的随机数据,代码很简单:

 

# 数据由random模块生成

data = [19, 9, 4, 10, 11]

2. 堆可视化工具:heapq_showtree.py

为了更直观地看到堆的结构,咱们用这个工具打印堆。代码如下:

 

import math

from io import StringIO

def show_tree(tree, total_width=36, fill=' '):

    """美化打印堆的结构"""

    output = StringIO()

    last_row = -1

    for i, n in enumerate(tree):

        if i:

            # 计算当前节点所在的“行”(树的层级)

            row = int(math.floor(math.log(i + 1, 2)))

        else:

            row = 0  # 根节点在第0行

        if row != last_row:

            output.write('\n')  # 换行开始新的一行

        # 计算每列的宽度,保证堆的结构对齐

        columns = 2 ** row

        col_width = int(math.floor(total_width / columns))

        output.write(str(n).center(col_width, fill))  # 居中显示节点值

        last_row = row

    print(output.getvalue())

    print('-' * total_width)  # 打印分隔线

    print()

四、创建堆:两种核心方法

有了基础数据和工具,咱们就来实战创建堆。heapq提供了两种常用方式:heappush()heapify(),各自适用不同场景。

1. 逐个添加:heappush ()

如果数据是动态生成的(比如从文件 / 接口逐步读取),用heappush()逐个添加元素,能实时维持堆的排序结构。

示例代码:heapq_heappush.py

 

import heapq

from heapq_showtree import show_tree  # 导入可视化工具

from heapq_heapdata import data  # 导入示例数据

heap = []

print('原始数据 :', data)

print()

# 逐个将数据加入堆

for n in data:

    print(f'添加元素 {n:>3}:')

    heapq.heappush(heap, n)  # 核心操作:将n加入堆

    show_tree(heap)  # 打印当前堆的结构

运行结果(终端输入python3 heapq_heappush.py):

 

原始数据 : [19, 9, 4, 10, 11]

添加元素  19:

                 19

------------------------------------

添加元素   9:

                 9

        19

------------------------------------

添加元素   4:

                 4

        19                9

------------------------------------

添加元素  10:

                 4

        10                9

    19

------------------------------------

添加元素  11:

                 4

        10                9

    19       11

------------------------------------

能看到每次添加元素后,堆都会自动调整,始终保持 “父节点≤子节点” 的最小堆结构~

2. 原地调整:heapify ()

如果数据已经全部在内存里(比如一个现成的列表),用heapify()直接原地调整列表为堆,效率比heappush()逐个添加更高。

示例代码:heapq_heapify.py

 

import heapq

from heapq_showtree import show_tree

from heapq_heapdata import data

print('原始数据    :', data)

heapq.heapify(data)  # 核心操作:原地将列表转为堆

print('调整后的堆 :')

show_tree(data)

运行结果(终端输入python3 heapq_heapify.py):

 

原始数据    : [19, 9, 4, 10, 11]

调整后的堆 :

                 4

        9                 19

    10       11

------------------------------------

注意哦!heapify()的结果和 “逐个heappush()” 最终得到的堆结构是一致的,但效率更高,推荐在数据已就绪时使用~

五、访问堆内容:取出最小元素

堆建好后,最常用的操作就是取出 “最小元素”(因为heapq是最小堆),核心方法是heappop()

示例代码:heapq_heappop.py

 

import heapq

from heapq_showtree import show_tree

from heapq_heapdata import data

print('原始数据    :', data)

heapq.heapify(data)

print('调整后的堆 :')

show_tree(data)

print()

# 两次弹出最小元素

for i in range(2):

    smallest = heapq.heappop(data)  # 核心操作:弹出最小元素

    print(f'弹出最小元素 {smallest:>3}:')

    show_tree(data)

运行结果(终端输入python3 heapq_heappop.py):

 

原始数据    : [19, 9, 4, 10, 11]

调整后的堆 :

                 4

        9                 19

    10       11

------------------------------------

弹出最小元素   4:

                 9

        10                19

    11

------------------------------------

弹出最小元素   9:

                 10

        11                19

------------------------------------

每次heappop()后,堆都会自动重新调整结构,确保下一次弹出的还是当前最小元素。利用这个特性,咱们甚至可以用 “heapify()+ 多次heappop()” 来实现列表排序~

六、替换元素:heapreplace ()

如果想 “删除堆中最小元素,同时插入新元素”,不用分两步(先heappop()heappush()),直接用heapreplace()一步搞定,效率更高。

示例代码:heapq_heapreplace.py

 

import heapq

from heapq_showtree import show_tree

from heapq_heapdata import data

heapq.heapify(data)

print('初始堆:')

show_tree(data)

# 替换两次元素

for n in [0, 13]:

    smallest = heapq.heapreplace(data, n)  # 核心操作:弹出最小+插入新元素

    print(f'用 {n:>2} 替换 {smallest:>2}:')

    show_tree(data)

运行结果(终端输入python3 heapq_heapreplace.py):

 

初始堆:

                 4

        9                 19

    10       11

------------------------------------

  0 替换  4:

                 0

        9                 19

    10       11

------------------------------------

用 13 替换  0:

                 9

        10                19

    13       11

------------------------------------

这个方法特别适合维护 “固定大小的堆”,比如 “优先级任务队列”—— 始终只保留优先级最高的 N 个任务,新任务进来时直接替换掉当前优先级最低的任务。

七、快速找极值:nlargest () 与 nsmallest ()

如果想从一堆数据里快速找出 “前 K 个最大元素” 或 “前 K 个最小元素”,heapqnlargest()nsmallest()能帮上忙。

示例代码:heapq_extremes.py

 

import heapq

from heapq_heapdata import data

print('全部数据       :', data)

print('前3个最大元素 :', heapq.nlargest(3, data))  # 前3大

print('排序后取前3大 :', list(reversed(sorted(data)[-3:])))  # 对比:排序法

print('前3个最小元素:', heapq.nsmallest(3, data))  # 前3小

print('排序后取前3小 :', sorted(data)[:3])  # 对比:排序法

运行结果(终端输入python3 heapq_extremes.py):

 

全部数据       : [19, 9, 4, 10, 11]

前3个最大元素 : [19, 11, 10]

排序后取前3大 : [19, 11, 10]

前3个最小元素: [4, 9, 10]

排序后取前3小 : [4, 9, 10]

小贴士:nlargest()nsmallest()只在 K 较小时(比如 K=3、5)效率高;如果 K 接近数据总量,直接用sorted()排序后切片反而更快哦~

八、高效合并有序序列:merge ()

如果有多个 “已经排好序的序列”,想把它们合并成一个新的有序序列,heapq.merge()是个好选择 —— 它不用先把所有数据合并再排序,而是用堆动态取最小元素,内存占用特别低。

示例代码:heapq_merge.py

 

import heapq

import random

random.seed(2016)  # 固定随机种子,保证结果可复现

# 生成4个“已排序的随机序列”

data = []

for i in range(4):

    new_data = list(random.sample(range(1, 101), 5))  # 1-100中随机选5个数

    new_data.sort()  # 排序,模拟“已有序的序列”

    data.append(new_data)

# 打印原始的4个有序序列

for i, d in enumerate(data):

    print(f'{i}: {d}')

# 合并序列并打印

print('\n合并后的有序序列:')

for i in heapq.merge(*data):  # 核心操作:合并多个有序序列

    print(i, end=' ')

print()

运行结果(终端输入python3 heapq_merge.py):

 

0: [33, 58, 71, 88, 95]

1: [10, 11, 17, 38, 91]

2: [13, 18, 39, 61, 63]

3: [20, 27, 31, 42, 45]

合并后的有序序列:

10 11 13 17 18 20 27 31 33 38 39 42 45 58 61 63 71 88 91 95

关键优势:merge()的内存占用只和 “序列个数” 有关,和每个序列的长度无关。哪怕每个序列有 100 万条数据,合并时也只需要维护 4 个元素的堆,超适合大数据场景!

总结:heapq 模块核心用法

最后给大家整理一下heapq的核心功能,方便记忆:

1.创建堆heappush()(逐个加)、heapify()(原地调);

2.取最小元素heappop()

3.替换元素heapreplace()(弹最小 + 插新);

4.找极值nlargest(K, 数据)(前 K 大)、nsmallest(K, 数据)(前 K 小);

5.合并有序序列merge(*序列们)(低内存)。

 


【声明】内容源于网络
0
0
我爱数据科学
精通R语言及Python,传递数据挖掘及可视化技术,关注机器学习及深度学习算法及实现,分享大模型及LangChain的使用技巧。编著多本R语言、python、深度学习等书籍。
内容 322
粉丝 0
我爱数据科学 精通R语言及Python,传递数据挖掘及可视化技术,关注机器学习及深度学习算法及实现,分享大模型及LangChain的使用技巧。编著多本R语言、python、深度学习等书籍。
总阅读103
粉丝0
内容322