一种著名的数据结构是堆(heap),它是一种优先队列。优先队列让你能够以任意顺序添加对象,并随时(可能是在两次添加对象之间)找出(并删除)最小的元素。相比于列表方法min,这样做的效率要高得多。
实际上,Python没有独立的堆类型,而只有一个包含一些堆操作函数的模块。这个模块名为heapq(其中的q表示队列),它包含6个函数,其中前4个与堆操作直接相关。必须使用列表来表示堆对象本身。
模块heapq中一些重要的函数
函 数 | 描 述 |
---|---|
heappush(heap, x) | 将x压入堆中 |
heappop(heap) | 从堆中弹出最小的元素 |
heapify(heap) | 让列表具备堆特征 |
heapreplace(heap, x) | 弹出最小的元素,并将x压入堆中 |
heappushpop(heap, x) | 将x压入栈中,并弹出最小的元素 |
nlargest(n, iter, key) | 返回iter中n个最大的元素 |
nsmallest(n, iter, key) | 返回iter中n个最小的元素 |
heapq.merge(*heap) | 将多个列表合并,并进行堆调整,返回的是合并后的列表的迭代器 |
函数heappush
用于在堆中添加一个元素。请注意,不能将它用于普通列表,而只能用于使用各种堆函数创建的列表。原因是元素的顺序很重要(虽然元素的排列顺序看起来有点随意,并没有严格地排序)。
>>> from heapq import *
>>> from random import shuffle
>>> data = list(range(10))
>>> shuffle(data)
>>> heap = []
>>> for n in data:
... heappush(heap, n)
...
>>> heap
[0, 1, 3, 6, 2, 8, 4, 7, 9, 5]
>>> heappush(heap, 0.5)
>>> heap
[0, 0.5, 3, 6, 1, 8, 4, 7, 9, 5, 2]
元素的排列顺序并不像看起来那么随意。它们虽然不是严格排序的,但必须保证一点:位置i处的元素总是大于位置i // 2处的元素(反过来说就是小于位置2 * i和2 * i + 1处的元素)。这是底层堆算法的基础,称为堆特征(heap property)。
函数heappop
弹出最小的元素(总是位于索引0处),并确保剩余元素中最小的那个位于索引0处(保持堆特征)。虽然弹出列表中第一个元素的效率通常不是很高,但这不是问题,因为heappop会在幕后做些巧妙的移位操作。
>>> heappop(heap)
0
>>> heappop(heap)
0.5
>>> heappop(heap)
1
>>> heap
[2, 5, 3, 6, 9, 8, 4, 7]
函数heapify
通过执行尽可能少的移位操作将列表变成合法的堆(即具备堆特征)。如果你的堆并不是使用heappush创建的,应在使用heappush和heappop之前使用这个函数。
>>> heap = [5, 8, 0, 3, 6, 7, 9, 1, 4, 2]
>>> heapify(heap)
>>> heap
[0, 1, 5, 3, 2, 7, 9, 8, 4, 6]
函数heapreplace
用得没有其他函数那么多。它从堆中弹出最小的元素,再压入一个新元素。相比于依次执行函数heappop和heappush,这个函数的效率更高。
>>> heapreplace(heap, 0.5)
0
>>> heap
[0.5, 1, 5, 3, 2, 7, 9, 8, 4, 6]
>>> heapreplace(heap, 10)
0.5
>>> heap
[1, 2, 5, 3, 6, 7, 9, 8, 4, 10]
nlargest(n, iter)
和nsmallest(n, iter)
,:分别用于找出可迭代对象iter中最大和最小的n个元素。这种任务也可通过先排序(如使用函数sorted)再切片来完成,但堆算法的速度更快,使用的内存更少(而且使用起来也更容易)。
无key
输出结果:
有key
输出结果:
merge(*heap):
用于将多个已排序的可迭代对象(例如列表、元组等)合并成一个排序后的迭代器。
这个函数主要用于合并两个或多个已排序的序列,而不需要事先将它们合并成一个大的序列,从而节省了内存空间。它通过一个堆数据结构来实现,在每次迭代中选择最小的元素,并将其从堆中取出,然后再选择下一个最小的元素。这种方法保证了合并后的结果是有序的。
import heapq
# 定义几个已排序的列表
list1 = [1, 3, 5, 7]
list2 = [2, 4, 6, 8]
list3 = [0, 9, 10, 11]
# 使用 heapq.merge 合并这些列表
merged = heapq.merge(list1, list2, list3)
# 遍历合并后的结果
for num in merged:
print(num, end=" ")
# 输出:0 1 2 3 4 5 6 7 8 9 10 11
在这个示例中,我们通过 heapq.merge 合并了三个已排序的列表 list1、list2 和 list3,并将结果迭代输出。可以看到,合并后的结果是一个按升序排列的迭代器。