堆是非线性的树形的数据结构,有2种堆,最大堆与最小堆。python的heapq模块默认的是最小堆。堆数据结构最重要的特征是heap[0] 永远是最小的元素。
最大堆:树中父节点的值总是大于等于任意子节点的值
最小堆:树中父节点的值总是大于等于任意子节点的值
我们一般使用二叉堆来实现优先级队列,它的内部调整算法复杂度为logN
1.堆排序的问题
1.1 利用heappush+heappop
import heapq
def sorted_func(iterable):
new_list = []
for element in iterable:
heapq.heappush(new_list, element)
return [heapq.heappop(new_list) for _ in range(len(new_list))]
if __name__ == '__main__':
list1 = [5, 8, 3, 9, 0, -3, 12, 4, 8]
print(sorted_func(list1))
result:
[-3, 0, 3, 4, 5, 8, 8, 9, 12]
1.2 利用nlargest或nsmallest
import heapq
list1 = [5, 8, 3, 9, 0, -3, 12, 4, 8]
print(heapq.nsmallest(len(list1), list1))
result:
[-3, 0, 3, 4, 5, 8, 8, 9, 12]
使用nlargest就相当于从大到小排序,和nlargest用法一致,这里就不再赘述
1.3 利用heapify
import heapq
list1 = [5, 8, 3, 9, 0, -3, 12, 4, 8]
heapq.heapify(list1)
print(list1)
result:
[-3, 0, 3, 4, 8, 5, 12, 9, 8]
2.优先级队列的问题
import heapq
class PriorityQueue(object):
def __init__(self):
self._queue = []
self.index = 0
def push(self, priority, item):
heapq.heappush(self._queue, (-priority, self.index, item))
self.index += 1
def pop(self):
return heapq.heappop(self._queue)[-1]
if __name__ == '__main__':
q = PriorityQueue()
q.push(2, 'two')
q.push(1, 'one')
q.push(-2, 'fei')
q.push(12, '123')
print(q.pop())
print(q.pop())
result:
123
two
在上述代码中,队列包含了(-priority, index, item)的元组。优先级为负数是为了从高到低进行排序。设置index变量是为了保证同等优先级元素的正确排序,这里相同优先级时会按照插入顺序进行排序(当然,你也可以按照插入顺序倒序排列,只要-index就可以。更或者可以自定义排序方式来替代index)。而且,index 变量也在相同 优先级元素比较的时候起到重要作用。
2.2 阐述说明
为了对这一点做出说明,先来看个例子,假设item不支持排序。
class Item:
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Item({!r})'.format(self.name)
a = Item('a')
b = Item('b')
print(a < b)
result:
Traceback (most recent call last):
File "D:/interview/interview/22.py", line 11, in <module>
print(a < b)
TypeError: '<' not supported between instances of 'Item' and 'Item'
2.2.1 (priority, item)二元元组的情形
如果你使用元组(priority, item) ,只要两个元素的优先级不同就能比较。但是如果两个元素优先级一样的话,那么比较操作就会跟之前一样出错
a = (1, Item('a'))
b = (2, Item('b'))
print(a < b)
result:
True
c = (1, Item('first'))
print(a < c)
result:
Traceback (most recent call last):
File "D:/interview/interview/22.py", line 13, in <module>
print(a < c)
TypeError: '<' not supported between instances of 'Item' and 'Item'
2.2.2 (priority, index, item)三元元组的情形
通过引入另外的index 变量组成三元组(priority, index, item) ,就能很好的避免上面的错误,因为不可能有两个元素有相同的index 值。Python 在做元组比较时候,如果前面的比较已经可以确定结果了,后面的比较操作就不会发生了。
a = (1, 0, Item('a'))
b = (2, 1, Item('b'))
print(a < b)
result:
True
c = (1, 2, Item('first'))
print(a < c)
result:
True
另外,如果你想在多个线程中使用同一个队列,那么你需要增加适当的锁和信号量机制。