“专业人士笔记”系列目录:创帆云:Python成为专业人士笔记--强烈建议收藏!每日持续更新!zhuanlan.zhihu.com
堆介绍
一种著名的数据结构是堆(heap),它是一种优先队列。优先队列让你能够以任意顺序添加对象,并随时(可能是在两次添加对象之间)找出(并删除)最小的元素。相比于列表方法min,这样做的效率要高得多。
实际上,Python没有独立的堆类型,而只有一个包含一些堆操作函数的模块。这个模块名为heapq(其中的q表示队列),它包含6个函数,其中前4个与堆操作直接相关。必须使用列表来表示堆对象本身。
堆类型类型中最大和最小值
要查找堆中最大的项,heapq模块有一个名为nlargest的函数,我们向它传递两个参数,第一个参数是要返回的元素个数,第二个参数是集合类型变量名
import heapq
numbers = [1, 4, 2, 100, 20, 50, 32, 200, 150, 8]
print(heapq.nlargest(4, numbers))
# 输出:[200, 150, 100, 50]
类似地,为了找到集合烦躁中最小的元素,我们使用 nsmallest函数:
import heapq
numbers = [1, 4, 2, 100, 20, 50, 32, 200, 150, 8]
print(heapq.nsmallest(4, numbers))
#输出:[1, 2, 4, 8】
对于复杂的数据结构,nlargest和 nsmallest 函数都采用一些可选的关键参数。下面的示例演示了如何使用age属性从people字典中检索最年长和最年轻的人
import heapq
people = [
{'firstname': 'John', 'lastname': 'Doe', 'age': 30},
{'firstname': 'Jane', 'lastname': 'Doe', 'age': 25},
{'firstname': 'Janie', 'lastname': 'Doe', 'age': 10},
{'firstname': 'Jane', 'lastname': 'Roe', 'age': 22},
{'firstname': 'Johnny', 'lastname': 'Doe', 'age': 12},
{'firstname': 'John', 'lastname': 'Roe', 'age': 45}
]
oldest = heapq.nlargest(2, people, key=lambda s: s['age'])
print(oldest)
# 输出: [{'firstname': 'John', 'age': 45, 'lastname': 'Roe'}, {'firstname': 'John', 'age': 30,'lastname': 'Doe'}]
#使用了key参数,利用匿名函数将key指定为每个字典元素的age属性
youngest = heapq.nsmallest(2, people, key=lambda s: s['age'])
print(youngest)
# 输出: [{'firstname': 'Janie', 'age': 10, 'lastname': 'Doe'}, {'firstname': 'Johnny', 'age': 12,'lastname': 'Doe'}]
#原理同上
最小元素弹出
堆最有趣的属性是它的最小元素始终是第一个元素:heap [0]
import heapq
numbers = [10, 4, 2, 100, 20, 50, 32, 200, 150, 8]
heapq.heapify(numbers) #将列表就地转换为堆
print(numbers)
# 输出: [2, 4, 10, 100, 8, 50, 32, 200, 150, 20] 堆的第一个元素一定是最小的那个值
heapq.heappop(numbers)
# 删除最小的值最返回:2
print(numbers)
# 输出: [4, 8, 10, 100, 20, 50, 32, 200, 150]
heapq.heappop(numbers)
# 删除最小的值最返回:4
print(numbers)
# 输出: [8, 20, 10, 100, 150, 50, 32, 200]
也许你看到每次输出堆的排列顺序非常随意,其实其并不像看起来的这样。它们虽然不是严格排序的,但必须保证一点:位置i处的元素总是大于位置i // 2处的元素(反过来说就是小于位置2 * i和2 * i + 1处的元素)。这是底层堆算法的基础,称为堆特征(heap property)。