Python基础(①⑤heapq模块)
堆(Heap)是什么?
堆是一种特殊的树状结构,最常见的是 “二叉堆”,它的核心特点是:
最顶端的元素是 “最值”(默认小顶堆的顶端是最小值,大顶堆则是最大值)。
往堆里添加 / 删除元素时,会自动调整结构,始终保持顶端是最值(这个过程叫 “堆化”)。
可以想象成一堆叠起来的盘子:
小顶堆:最小的盘子必须放在最上面,不管你怎么添新盘子或拿走最上面的,最后最上面的一定还是最小的。
大顶堆:最大的盘子放在最上面,同理。
用途:高效找最值、实现优先队列、Top K 问题等(比如用 heapq 模块快速找前几名最小 / 最大的元素)。
最常用的 3 个功能:
heapify():把列表变成堆(自动调整顺序)
heappop():弹出并返回堆中最小的元素(弹出后会自动调整堆结构)
heappush():往堆里添加一个元素(添加后会自动调整)
import heapq# 1. 准备一个普通列表
nums = [3, 1, 4, 2, 5]# 2. 把列表转换成堆(小顶堆)
heapq.heapify(nums)
print("转换后的堆:", nums) # 输出 [1, 2, 4, 3, 5](最小的1在最前面)# 3. 弹出最小的元素
smallest = heapq.heappop(nums)
print("弹出的最小元素:", smallest) # 输出 1
print("弹出后剩下的堆:", nums) # 输出 [2, 3, 4, 5](自动调整好了)# 4. 往堆里添加一个元素
heapq.heappush(nums, 0)
print("添加0后的堆:", nums) # 输出 [0, 2, 4, 5, 3](0成了新的最小元素)
Top K 问题
核心算法:
找前 K 个最大元素:用小顶堆(堆里只存 K 个元素,堆顶是当前第 K 大,新元素比堆顶大就替换)。
找前 K 个最小元素:用大顶堆(同理,堆顶是当前第 K 小)。
代码示例(从 100 万个随机数中找最大的 3 个):
import heapq
import random# 生成100万个随机数
data = [random.randint(0, 1000000) for _ in range(1000000)]
k = 3# 用小顶堆找前3个最大的数
heap = data[:k]
heapq.heapify(heap) # 初始化小顶堆(堆顶是当前最小的“候选者”)# 遍历剩余数据
for num in data[k:]:if num > heap[0]: # 新数比堆顶大,说明堆顶不够格heapq.heappop(heap) # 移除堆顶heapq.heappush(heap, num) # 加入新数print(f"最大的3个数:{sorted(heap, reverse=True)}") # 从大到小输出
性能对比表(N 是数据总量,K 是要找的前 K 个)
指标 | 直接排序法 | 堆解法(Top K) | 差距(N 很大时) |
---|---|---|---|
时间复杂度 | O(N log N) | O(N log K) | 堆解法快很多(比如 K=10 时,log K≈3,log N 可能是 20+) |
空间复杂度 | O(N) | O(K) | 堆解法节省大量内存(比如 N=100 万,K=10 时,省 99.99% 空间) |
适用场景 | 数据量小、需全量排序 | 海量数据、只需前 K 个 | 堆解法更适合大数据场景 |
数据流中的中位数
import heapqclass MedianFinder:def __init__(self):self.small = [] # 大顶堆(存较小的一半,用负数模拟大顶堆)self.large = [] # 小顶堆(存较大的一半)def addNum(self, num):# 先加入大顶堆,再平衡两个堆的大小heapq.heappush(self.small, -num) # 负数模拟大顶堆# 确保大顶堆的最大值 <= 小顶堆的最小值if self.small and self.large and -self.small[0] > self.large[0]:val = -heapq.heappop(self.small)heapq.heappush(self.large, val)# 平衡两个堆的大小(差距不超过1)if len(self.small) > len(self.large) + 1:val = -heapq.heappop(self.small)heapq.heappush(self.large, val)elif len(self.large) > len(self.small) + 1:val = heapq.heappop(self.large)heapq.heappush(self.small, -val)def findMedian(self):if len(self.small) > len(self.large):return -self.small[0]elif len(self.large) > len(self.small):return self.large[0]else:return (-self.small[0] + self.large[0]) / 2# 测试
mf = MedianFinder()
mf.addNum(1)
mf.addNum(3)
mf.addNum(2)
print("中位数:", mf.findMedian()) # 输出 2
mf.addNum(4)
print("中位数:", mf.findMedian()) # 输出 2.5((2+3)/2)
123