一、什么是双端队列
在介绍Python中的双端队列(deque)方法之前,我们先来了解一下什么是双端队列。
双端队列(Deque),全称为双端队列(Double-Ended Queue),是一种具有队列和栈的性质的数据结构。
它的两端都可以执行插入和删除操作,因此它可以在队列头部和尾部两端进行插入和删除元素的操作。
它的特点是:插入和删除元素的时间复杂度都是O(1)。
from collections import deque
# 创建双端队列
my_deque = deque()
# 在队列的左侧插入一个元素
my_deque.appendleft('a')
# 在队列的右侧插入一个元素
my_deque.append('b')
# 在队列左侧删除一个元素,并返回该元素的值
left_pop = my_deque.popleft()
# 在队列右侧删除一个元素,并返回该元素的值
right_pop = my_deque.pop()
二、双端队列的常用操作方法
1. append(item) 方法
在队列的右侧插入一个元素。
from collections import deque
# 创建双端队列
my_deque = deque()
# 在队列的右侧插入一个元素
my_deque.append('a')
2. appendleft(item) 方法
在队列的左侧插入一个元素。
from collections import deque
# 创建双端队列
my_deque = deque()
# 在队列的左侧插入一个元素
my_deque.appendleft('a')
3. clear() 方法
清空队列中的所有元素。
from collections import deque
# 创建双端队列,插入几个元素
my_deque = deque(['a', 'b', 'c'])
# 清空队列
my_deque.clear()
4. count(item) 方法
统计队列中某个元素的出现次数。
from collections import deque
# 创建双端队列,插入几个元素
my_deque = deque(['a', 'b', 'b', 'c', 'd'])
# 统计元素'b'的出现次数
b_count = my_deque.count('b')
5. extend(iterable) 方法
在队列的右侧依次插入可迭代对象中的所有元素。
from collections import deque
# 创建双端队列,插入几个元素
my_deque = deque(['a', 'b'])
# 将另一个可迭代对象的元素插入到队列的右侧
my_deque.extend(['c', 'd'])
6. extendleft(iterable) 方法
在队列的左侧依次插入可迭代对象中的所有元素。需要注意的是,元素的顺序与可迭代对象的顺序相反。
from collections import deque
# 创建双端队列,插入几个元素
my_deque = deque(['a', 'b'])
# 将另一个可迭代对象的元素插入到队列的左侧
my_deque.extendleft(['c', 'd']) # 结果为 deque(['d', 'c', 'a', 'b'])
7. index(item, start=0, stop=len(queue)) 方法
在队列中查找某个元素的位置,并返回它第一次出现的索引。
可以指定查找的区间,start和stop参数分别表示查询区间的起始和终止位置,如果不指定,则默认搜索整个队列。
from collections import deque
# 创建双端队列,插入几个元素
my_deque = deque(['a', 'b', 'c', 'd', 'e'])
# 查找元素'd'在队列中第一次出现的索引
d_index = my_deque.index('d')
# 查找元素'd'在队列中从第3个元素(不包括第3个元素)到队列末尾的位置
d_index = my_deque.index('d', 3)
8. insert(index, item) 方法
在队列的指定位置插入一个元素。
from collections import deque
# 创建双端队列,插入几个元素
my_deque = deque(['a', 'b', 'c'])
# 在指定位置插入元素
my_deque.insert(1, 'd') # 结果为 deque(['a', 'd', 'b', 'c'])
9. pop() 方法
从队列的右侧删除一个元素,并返回该元素的值。
from collections import deque
# 创建双端队列,插入几个元素
my_deque = deque(['a', 'b', 'c'])
# 从右侧删除一个元素并返回该元素的值
right_pop = my_deque.pop()
10. popleft() 方法
从队列的左侧删除一个元素,并返回该元素的值。
from collections import deque
# 创建双端队列,插入几个元素
my_deque = deque(['a', 'b', 'c'])
# 从左侧删除一个元素并返回该元素的值
left_pop = my_deque.popleft()
11. remove(item) 方法
从队列中删除某个元素,如果有多个,则删除第一个。
from collections import deque
# 创建双端队列,插入几个元素
my_deque = deque(['a', 'b', 'c', 'b'])
# 删除第一个元素'b'
my_deque.remove('b')
12. reverse() 方法
翻转队列中的元素。
from collections import deque
# 创建双端队列,插入几个元素
my_deque = deque(['a', 'b', 'c'])
# 反转队列中的元素
my_deque.reverse() # 结果为 deque(['c', 'b', 'a'])
三、双端队列使用场景举例
双端队列可以在很多场景中使用,下面以Python中的实现为例:
1. 最近最少使用(LRU)缓存算法
在Python中,collections模块中的OrderedDict类就是一个经典的LRU缓存算法的实现。
OrderedDict类继承了Python内置字典类的所有方法,同时还实现了一个__setitem__方法,用于在向字典中插入新元素时维护元素的顺序。
当缓存达到最大容量时,在向OrderedDict中添加新的元素时,先删除最早被访问的元素,然后再添加新元素。
from collections import OrderedDict
# 创建最大容量为2的OrderedDict对象
cache = OrderedDict(maxlen=2)
# 向缓存中添加元素
cache[1] = 'a'
cache[2] = 'b'
# 打印当前缓存中的元素顺序
print(cache.items()) # 结果为 dict_items([(1, 'a'), (2, 'b')])
# 再添加一个元素,由于缓存大小只有2,因此会删除最早被访问的元素1
cache[3] = 'c'
# 打印当前缓存中的元素顺序
print(cache.items()) # 结果为 dict_items([(2, 'b'), (3, 'c')])
2. 广度优先搜索算法
在广度优先搜索算法中,需要使用队列来维护待访问的节点集合,而双端队列可以同时在队列的头尾进行插入和删除操作,非常适合用于实现广度优先搜索算法。
广度优先搜索算法可以应用于很多领域,包括自然语言处理、计算机视觉、图像处理等。
from collections import deque
# 定义图的邻接表表示
graph = {
'A':{'B'},
'B':{'C','D'},
'C':{'D'},
'D':{'C'},
'E':{'D'}
}
# 广度优先搜索算法,计算从起点'A'到终点'D'的最短路径
def bfs(start, end):
# 队列中保存路径
queue = deque()
queue.append([start])
# 保存已经遍历的节点
visited = set()
while queue:
# 取出路径
path = queue.popleft()
# 取出路径的最后一个节点
node = path[-1]
if node == end:
# 如果已经到达终点,则返回路径
return path
if node not in visited:
# 如果当前节点没有被访问过,则标记为已访问
visited.add(node)
# 在所有邻居中增加一条新路径,并把这些新路径全部加入到队列中
for neighbor in graph[node]:
new_path = path + [neighbor]
queue.append(new_path)
# 测试bfs函数
print(bfs('A', 'D')) # 结果为 ['A', 'B', 'D']
四、结语
利用双端队列可以快速高效地实现很多常见的数据结构和算法,比如实现队列、栈、LRU缓存算法和广度优先搜索算法等。
同时,Python中的collections模块中也提供了非常丰富的双端队列方法,帮助我们轻松地实现各种数据结构和算法,提高生产效率。