本文用于介绍python中内置的堆、栈和队列结构方法,并且计较这些方法的差异与使用场景。
heapq 堆队列
heapq
是一个内置堆结构,一种特殊形式的完全二叉树,其中父节点的值总是大于子节点,根据其性质,python可以用一个满足 heap[k] <= heap[2 * k + 1] <= heap[2 * k + 2]
的列表来实现。heapq
是最小堆,如果要实现最大堆,可以使用一些小诀窍,例如在heappush
的时候,填进去的是 数据 * -1,然后heappop
的时候,将弹出的元素乘以-1即可
import heapq
heap = []
heapq.heappush(heap, 3)
heapq.heappush(heap, 2)
heapq.heappush(heap, 1)
print(heap) # 输出为 [1, 3, 2]
# 要想有序输出 堆的元素
heapq.heappop(heap) # 输出1
heapq.heappop(heap) # 输出2
heapq.heappop(heap) # 输出3
# 要想输出前n个最大值,前n个最小值,可以使用
heapq.nlargest(n, heap)
heapq.nsmallest(n, heap)
deque 双端队列
from collections import deque
可以实现栈也可以实现队列的功能,因为双端队列可以在队列的头部和尾部进行编辑,所以我们如果想要在python中实现栈的功能的话,最好的选择是deque,当然我们也可以使用普通的数组结构。
普通的队列操作,就是在队尾插入元素,然后队头弹出元素:
dq = queue()
dq.append(3) # deque([3])
dq.append(4) # deque([3, 4])
dq.popleft() # 弹出 3,并且dq为 deque([4])
如果设置为一个栈的话,FILO,那么就是如下的代码
dq = queue()
dq.append(3) # deque([3])
dq.append(4) # deque([3, 4])
dq.pop() # 弹出 4,并且dq为 deque([3])
queue
queue队列是一个线程安全的包,这个包里面有很多的结构,比如栈 LifoQueue
,如果要在并发环境下使用的话,最好使用这个结构,因为如果栈为空的情况下,去弹出栈顶元素的话,会出现阻塞现象,知道栈不为空为止
LifoQueue
LifoQueue
是一个栈结构,有入栈和出战操作,方法分别是 put()
和 get()
,并且 get()
在 LifoQueue()
为空时会阻塞
from queue import LifoQueue
s = LifoQueue()
s.put(3)
s.put(4)
print(s.get()) # 打印4
print(s.get()) # 打印3
print(s.get()) # 一直等待直到s中存在元素,然后就打印退出
Queue
Queue
是一个队列,有入队和出队操作,方法分别是 put()
和 get()
,并且 get()
在 Queue
为空时会阻塞,另外也可以设置队列的长度,如果没有设置队列的长度,或者设置队列的长度为小于等于0的数,那么表示队列的长度为无限,可以通过 .maxsize
属性来获取队列的最大长度
from queue import Queue
q = Queue()
q.put(3)
q.put(4)
print(q.get()) # 打印3
print(q.get()) # 打印4
print(q.get()) # 一直等待直到q中存在元素,然后就打印退出
print(q.get_nowait()) # 如果队列q为空,那么不会等待,而是输出 Empty 的异常
PriorityQueue
PriorityQueue
是一个优先队列,队列的元素是排好序了的,所以针对一个排序的序列可以使用优先队列,它能高效获取最大或最小的元素。
在调度问题的场景中,经常用到优先级队列,它主要有获取最大值或最小值的操作和入队操作
优先队列,是内部封装了 heapq
,不同的是优先队列是线程安全的,在并发环境下应该选择使用 PriorityQueue
from queue import PriorityQueue
pq = PriorityQueue()
pq.put((2, 'name'))
pq.put((1, 'age'))
pq.put((3, 'job'))
while not pq.empty():
print(pq.get())
"""
输出内容如下
(1, "age")
(2, "name")
(3, "job")
"""
multiprocessing.Queue
多进程版本的队列 multiprocessing.Quque
,如果要在多进程环境下使用队列,那么应该选择 multiprocessing.Queue
同样的,它的入队出队操作分别是 put()
和 get()
。
汇总如下
对象 | 使用的包 | 使用场景 | 说明 |
---|---|---|---|
堆 | heapq |
单线程堆 | 最小堆 |
堆 | queue.PriorityQueue |
并发环境使用的堆 优先队列,用 .put(x) 和 .get() 实现入堆和出堆 |
最小堆 |
栈 | deque |
单线程栈 通过 .append(x) 和 .pop() 实现压栈和出栈FILO |
|
栈 | queue.LifoQueue |
并发环境使用的栈 通过 .put(x) 和 .get() 实现入栈和出栈 |
|
队列 | deque |
单线程队列 通过 .append(x) 和 .popleft() 实现入队和出队FIFO |
两头都可以插入和弹出 |
队列 | queue.Queue |
并发环境使用的队列 通过 .put(x) 和 .get() 实现入队和出队 |
|
队列 | multiprocessing.Queue |
多进程环境使用的队列 通过 .put(x) 和 .get() 实现入队和出队 |