Python3 cookbook学习笔记-数据结构与算法2

来源:互联网 发布:雨篷计算软件 编辑:程序博客网 时间:2024/05/16 07:56

查找最大或最小的N个元素

heapq模块有两个函数:nlargest()和nsmallest()可以完美解决这个问题。

>>> import heapq>>> nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]>>> print(heapq.nlargest(3,nums))[42, 37, 23]>>> print(heapq.nsmallest(3,nums))[-4, 1, 2]

两个函数都能接受一个关键字参数,用于更复杂的数据结构中:

>>> import heapq>>> portfolio = [...     {'name': 'IBM', 'shares': 100, 'price': 91.1},...     {'name': 'AAPL', 'shares': 50, 'price': 543.22},...     {'name': 'FB', 'shares': 200, 'price': 21.09},...     {'name': 'HPQ', 'shares': 35, 'price': 31.75},...     {'name': 'YAHOO', 'shares': 45, 'price': 16.35},...     {'name': 'ACME', 'shares': 75, 'price': 115.65}... ]>>> cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price'])>>> cheap[{'name': 'YAHOO', 'shares': 45, 'price': 16.35}, {'name': 'FB', 'shares': 200, 'price': 21.09}, {'name': 'HPQ', 'shares': 35, 'price': 31.75}]>>> expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price'])>>> expensive[{'name': 'AAPL', 'shares': 50, 'price': 543.22}, {'name': 'ACME', 'shares': 75, 'price': 115.65}, {'name': 'IBM', 'shares': 100, 'price': 91.1}]

上面代码在对每个元素进行对比的时候,会以 price的值进行比较。

如果你想在一个集合中查找最小或最大的N个元素,并且N小于集合元素数量,那么这些 函数提供了很好的性能。 因为在底层实现里面,首先会先将集合数据进行堆排序后放入 一个列表中:

>>> nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]>>> import heapq>>> nums[1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]>>> heapq.heapify(nums)>>> nums[-4, 2, 1, 23, 7, 2, 18, 23, 42, 37, 8]

堆数据结构最重要的特征是 heap[0]永远是最小的元素。并且剩余的元素可以很容易的 通过调用 heapq.heappop()方法得到, 该方法会先将第一个元素弹出来,然后用下一个最 小的元素来取代被弹出元素(这种操作时间复杂度仅仅是O(N),N是堆大小)。

当要查找的元素个数相对比较小的时候,函数 nlargest()和nsmallest() 是很合适的。 如果你仅仅想查找唯一的最小或最大(N=1)的元素的话,那么使用min()和max()函数会更
快些。 类似的,如果N的大小和集合大小接近的时候,通常先排序这个集合然后再使用切 片操作会更快点 (sorted(items)[:N] 或者是sorted(items)[-N:] )。 需要在正确场合使用 函数nlargest() 和 nsmallest()才能发挥它们的优势 (如果N快接近集合大小了,那么使用排 序操作会更好些)。

实现一个优先级队列

怎样实现一个按优先级排序的队列? 并且在这个队列上面每次pop操作总是返回优先级最高的那个元素?

>>> import heapq>>> class PriorityQueue:...     def __init__(self):...         self._queue = []...         self._index = 0...     def push(self, item, priority):...         heapq.heappush(self._queue, (-priority, self._index, item))...         self._index += 1...     def pop(self):...         return heapq.heappop(self._queue)[-1]...>>> class Item:...     def __init__(self, name):...         self.name = name...     def __repr__(self):...         return 'Item({!r})'.format(self.name)
>>> q = PriorityQueue()>>> q.push(Item('foo'),1)>>> q.push(Item('bar'),5)>>> q.push(Item('spam'), 4)>>> q.push(Item('grok'), 1)>>> q.pop()Item('bar')>>> q.pop()Item('spam')>>> q.pop()Item('foo')>>> q.pop()Item('grok')

个人心得:
初学这里有点不好理解,看了几遍官方文档,感觉主要还是要理解heapq的特点。上一个问题提到过:堆数据结构最重要的特征是 heap[0]永远是最小的元素。
1、heapq.heappush(self._queue, (-priority, self._index, item))
这一句,将数据push进堆中,这里的数据是一个由优先级、索引和item构成的元组;
优先级前面为什么加负号?从结果反推,优先级数值越大,优先级越高,而堆pop出来的总是最小的那个,所以加个负号,可以让它由最大变为最小。
2、heapq.heappop(self._queue)[-1]
push进堆的是一个元组,那么pop出来的数据也应该是一个元组,[-1]表示取元组的倒数第一个数据,即item.
3、def repr(self):
… return ‘Item({!r})’.format(self.name)
Item类中的这个函数,通过查资料得知,它的作用就是将类的实例转换为字符串。至于细节,咱不做考虑。

字典中的键映射多个值

怎样实现一个键对应多个值的字典?
一个字典就是一个键对应一个单值的映射。如果你想要一个键映射多个值,那么你就需要 将这多个值放到另外的容器中, 比如列表或者集合里面。
选择使用列表还是集合取决于你的实际需求。如果你想保持元素的插入顺序就应该使用列 表, 如果想去掉重复元素就使用集合(并且不关心元素的顺序问题)。
你可以很方便的使用collections 模块中的 defaultdict来构造这样的字典。

>>> from collections import defaultdict>>> d = defaultdict(list)>>> d['a'].append(1)>>> d['a'].append(2)>>> d['b'].append(4)>>> ddefaultdict(<class 'list'>, {'a': [1, 2], 'b': [4]})>>>>>> d = defaultdict(set)>>> d['a'].add(1)>>> d['a'].add(2)>>> d['b'].add(4)>>> ddefaultdict(<class 'set'>, {'a': {1, 2}, 'b': {4}})

需要注意的是, defaultdict会自动为将要访问的键(就算目前字典中并不存在这样的键) 创建映射实体。 如果你并不需要这样的特性,你可以在一个普通的字典上使用setdefault()
方法来代替。比如:

>>> d = {}>>> d{}>>> d.setdefault('a',[]).append(1)>>> d{'a': [1]}>>> d.setdefault('a',[]).append(2)>>> d{'a': [1, 2]}>>> d['a'].append(3)>>> d{'a': [1, 2, 3]}>>> d.setdefault('b',[]).append(4)>>> d{'a': [1, 2, 3], 'b': [4]}

个人心得:我个人并未理解到这两种方式的不同。

0 0
原创粉丝点击