【Python】堆和优先队列的使用

优先队列的例子,源自 23. 合并K个排序链表(我的解答)

python里面的堆是通过在列表中维护堆的性质实现的。

python堆的部分API,其他API查阅文档python_heap_API和 heapq的源代码

import heapq
#向堆中插入元素,heapq会维护列表heap中的元素保持堆的性质
heapq.heappush(heap, item)
#heapq把列表x转换成堆
heapq.heapify(x)
#从可迭代的迭代器中返回最大的n个数,可以指定比较的key
heapq.nlargest(n, iterable[, key])
#从可迭代的迭代器中返回最小的n个数,可以指定比较的key
heapq.nsmallest(n, iterable[, key])
#从堆中删除元素,返回值是堆中最小或者最大的元素
heapq.heappop(heap)

优先队列 

PriorityQueue使用的就是heapq来实现的,所以可以认为两者算法本质上是一样的。当然PriorityQueue考虑到了线程安全的问题。 

#向队列中添加元素
Queue.put(item[, block[, timeout]])
#从队列中获取元素
Queue.get([block[, timeout]])
#队列判空
Queue.empty()
#队列大小
Queue.qsize()
# 优先队列总是会弹出当前元素中优先级最高的。当插入元素时,将其插入到满足当前
# 优先级顺序的位置。优先队列是一种重要的缓存结构。
# 当用列表来实现优先队列时,可以考虑利用元素在列表中的先后顺序来表示优先级关系。
# 为了在O(1)时间内弹出优先级最高的元素,
# 所以应该将优先级最高的元素放在列表的表尾。 
# 在以下的实现中,值较小的元素具有更高的优先级。

if __name__ == "__main__":
    temp = Pri_Queue(['a', 'b', 'g', 'd'])
    temp.enqueue('f')
    print(temp._elems) # ['g', 'f', 'd', 'b', 'a']
    print(temp.peek()) # a
    temp.dequeue()
    print(temp._elems) # ['g', 'f', 'd', 'b']

q = queue.PriorityQueue()

q.put()调用的是heapq.heappush(heap, item),而headpush()方法则是调用每个item的比较大小方法,默认是小的在顶端,所以是小顶堆。所以能用元组自定义插入顺序,

比如(2,item1)<(3,item2),

所以q.put((2.item1)),q.put((3,item2)),q.get()输出为(2,item1)。

之前想复杂了,认为元组作为参数时会发生其他的事,看了下源码才发现就是把元组当做一个整体item进行大小的比较,并没有什么不同。任何对象只要实现了比较的方法,都能作为q.put的参数。


api使用:

# API使用
from queue import PriorityQueue
# 默认是小的在顶端,所以是小顶堆
q = PriorityQueue()
# #队列判空
print(q.empty()) # True
# #向队列中添加元素
q.put((1,'aa'))
q.put((0,'bb'))
q.put((100,'cc'))
q.put((50,'dd'))
# #队列大小
print(q.qsize()) # 4
# 从队列中获取元素
for _ in range(q.qsize()):
    print(q.get()) # get()的时候是,每个元素真实的出了队列
    # (0, 'bb')
    # (1, 'aa')
    # (50, 'dd')
    # (100, 'cc')
print(q.empty()) # True
q.put((98,'ss'))
print(q.get()) # (98, 'ss')
    from heapq import heappush, heappop
    heap = []
    # 实现小顶堆
    # 向堆中插入元素,heapq会维护列表heap中的元素保持堆的性质
    heappush(heap,(1,'aa'))
    heappush(heap,(0,'bb'))
    heappush(heap,(100,'cc'))
    heappush(heap,(50,'dd'))
    print(heap) # [(0, 'bb'), (1, 'aa'), (100, 'cc'), (50, 'dd')]
    #从堆中删除元素,返回值是堆中最小
    print(heappop(heap)) # (0, 'bb')
    print(heap) # [(1, 'aa'), (50, 'dd'), (100, 'cc')],维护了堆的性质
    print(heappop(heap)) # (1, 'aa')
    print(heappop(heap)) # (50, 'dd')

每次pop都是最小的元素,如何实现大顶堆,返回最大的元素呢?

# 每次pop都是最小的元素,如何实现大顶堆,返回最大的元素呢
b = []
heapq.heappush(b,-1)
heapq.heappush(b,-100)
heapq.heappush(b,-66)
heapq.heappush(b,-12)
heapq.heappush(b,-200)
print(b) # [-200, -100, -66, -1, -12]
print(-heapq.heappop(b)) # 200
print(-heapq.heappop(b)) # 100
print(-heapq.heappop(b)) # 66
print(-heapq.heappop(b)) # 12

实现大顶堆,返回最大的元素,概括一种最简单的:

将push(e)改为push(-e)、pop(e)改为-pop(e)。

也就是说,在存入堆、从堆中取出的时候,都用相反数,而其他逻辑与TopK完全相同

# 实现大顶!d=====( ̄▽ ̄*)b
# 加入的时候添加负号,从API默认的小顶堆变成大顶堆
heapBIG = []
heappush(heapBIG,(-1,'aa'))
heappush(heapBIG,(-0,'bb'))
heappush(heapBIG,(-100,'cc'))
heappush(heapBIG,(-50,'dd'))
print(heapBIG) # [(-100, 'cc'), (-50, 'dd'), (-1, 'aa'), (0, 'bb')]
#从堆中删除元素,返回值是堆中最大
print(heappop(heapBIG)) # (-100, 'cc')
print(heapBIG) # [(-50, 'dd'), (0, 'bb'), (-1, 'aa')]
print(heappop(heapBIG)) # (-50, 'dd')
print(heappop(heapBIG)) # (-1, 'aa')
    import heapq
    heap = []
    # #向堆中插入元素,heapq会维护列表heap中的元素保持堆的性质
    heapq.heappush(heap,(1,'aa'))
    heapq.heappush(heap,(0,'bb'))
    heapq.heappush(heap,(100,'cc'))
    heapq.heappush(heap,(50,'dd'))
    print(heap) # [(0, 'bb'), (1, 'aa'), (100, 'cc'), (50, 'dd')]
    # heapq把列表x转换成堆
    heapq.heapify(heap)
    print(heap) # [(0, 'bb'), (1, 'aa'), (100, 'cc'), (50, 'dd')]
    # print(heapq.nsmallest(heapq)) # TypeError: nsmallest() missing 1 required positional argument: 'iterable'
    # 从可迭代的迭代器中返回最大的n个数,可以指定比较的key
    print(heapq.nlargest(2,range(5))) # [4, 3]
    # 从可迭代的迭代器中返回最小的n个数,可以指定比较的key
    print(heapq.nsmallest(2,range(5))) # [0, 1]
    # 从堆中删除元素,返回值是堆中最小
    print(heapq.heappop(heap)) # (0, 'bb')
    print(heap) # [(1, 'aa'), (50, 'dd'), (100, 'cc')]

参考:

python 堆和优先队列的使用

https://blog.csdn.net/sinat_41086096/article/details/79429744 

Python使用heapq实现小顶堆(TopK大)、大顶堆(BtmK小)

你可能感兴趣的:(编程语言,数据结构)