首先,堆是一种优先队列,能够以任意顺序添加对象,并随时(可能是在两次添加对象之间)找出(并删除)最小的元素。类似于列表方法min,但效率要高很多。
其实在python中没有独立的堆类型,而只有一个包含一些堆操作函数的模块。这个模块名为heapq。
堆特征 -> 元素不是严格,但必须保证一点:位置i处的元素总是大于位置 i // 2处的元素(反过来就是小于位置2 * i 和 2 * i + 1处的元素,i从1开始计数,即heap[0]为第一个元素)。
1.堆的主要函数及详解
In [34]: import heapq
In [35]: heapq.__all__
Out[35]:
['heappush', 'heappop', 'heapify', 'heapreplace', 'merge', 'nlargest', 'nsmallest', 'heappushpop']
函数 | 描述 |
---|---|
heappush(heap, item, /) | 将item压入堆中 |
heappop(heap, /) | 从堆中弹出最小的元素 |
heapify(heap, /) | 让列表具有堆的特性,线性时间复杂度 |
heapreplace(heap, item, /) | 弹出最小的元素,并将item压入堆中 |
merge(*iterables, key=None, reverse=False) | 将多个排序的输入合并为一个排序的输出。 |
nlargest(n, iterable, key=None) | 返回iterable中n个最大的元素 |
nsmallest(n, iterable, key=None) | 返回iterable中n个最小的元素 |
2.堆函数的应用
heappush && heappop:压入和弹出元素
heappop弹出最小的元素,并确保剩余元素中最小的那个位于索引0处(保持堆特征)
from heapq import *
from random import shuffle
data = list(range(10))
shuffle(data)
heap = []
for n in data:
heappush(heap, n)
print(heap) # [0, 1, 3, 4, 2, 8, 6, 7, 5, 9],严格满足heap[i] > heap[i//2]
print(heappop(heap)) # 0
print(heappop(heap)) # 1
print(heappop(heap)) # 2
print(heap) # [3, 6, 4, 7, 8, 9, 5],移除元素的堆仍然具有堆特征
heapify :将列表变为合法堆
如果你的堆不是通过heappush创建的,而是自己手动构造的,请在heappush和heappop之前使用这个函数
from heapq import *
heap1 = [3, 4, 2, 6, 7, 1, 0, 9]
heapify(heap1)
print(heap1) # [0, 4, 1, 6, 7, 3, 2, 9]
heaprepalce:从堆中弹出最小的元素,并压入一个元素
相比于依次执行函数heappop和heappush,这个函数的效率更高
from heapq import *
heap2 = [3, 4, 2, 6, 7, 1, 0, 9]
heapreplace(heap2, 8)
print(heap2) # [1, 4, 2, 6, 7, 3, 8, 9]
nlargest && nsmallest
这两个函数分别用于找出可迭代对象iter中最大和最小的n个元素。这种任务也可通过先排序(如使用函数sorted)再切片再完成,但堆算法的速度更快,使用的内存更少(而且使用起来也更容易)。
L = [3, 4, 2, 7, 8, 0, 11, 5]
print(sorted(L)) # [0, 2, 3, 4, 5, 7, 8, 11]
print(nlargest(3, L)) # [11, 8, 7] 从大到小的顺序
print(nsmallest(3, L)) # [0, 2, 3]