1.什么是双端队列

deque的英文意思是Double-Ended Queue,从字面的意思来看,他就是一个双向队列。我们使用list存储数据的时候,按索引访问元素很快,因为list是线性存储,数据量很大的时候在列表头插入和删除元素的效率就会很慢。为什么list效率低呢?

  • 因为list有append()和insert(index,value)两个添加方法,append()方法只能在在列表的尾部追加元素,而insert(index)虽然能在指定的位置去添加元素,但是他需要去遍历list才行所以时间复杂度为o(N)。
  • 而list中的删除有del names[index],pop()或者pop(index),remove(value)可以看出list删除除了pop()[删除列表末尾的元素]之外,剩下的都需要去遍历list列表,所以时间复杂度是o(N)。

deque是为了在两端高效实现插入和删除操作的双向列表,适合用于队列和栈:deque除了实现list的append()和pop()外,还支持appendleft()和popleft(),这样就可以非常高效地往头部或者尾部添加或删除元素。

2.列表与双端队列

双端队列支持线程安全,在双端队列的任何一端执行添加和删除操作,它们的内存效率几乎相同(时间复杂度为O(1))。

虽然list也支持类似的操作,但是它对定长列表的操作表现很不错,而当遇到pop(0)和insert(0, v)这样既改变了列表的长度又改变其元素位置的操作时,其时间复杂度就变为O(n)了。

在双端队列中最好不使用切片( 如果使用deque进行切片的话会抛出异常)和索引( 和列表一样的使用,虽然效果上是一样的,但是可能效率上还是列表的索引效率更高一些),你可以用popleft和appendleft方法,双端队列对这些操作做了优化。在两端的索引访问时间复杂度为O(1),但是访问中间元素的时间复杂度为O(n),速度较慢,对于快速随机的访问,还是用列表代替。

列表用于随机访问和定长数据的操作,包括切片,而双端队列适用于在两端压入或弹出元素,索引的效率可能低于列表,同时也不支持切片。

3.双端队列的使用

python flask 线程ID_双端队列

 

extendleft()方法,他是把列表中的元素进行迭代,先取出第一个元素,然后放在左边,然后再去取出下一个,重复执行,就得到了最终的结果。

from collections import deque
d = deque([1,2,3])
d.extendleft(['a','b','c'])
print(d)

deque(['c', 'b', 'a', 1, 2, 3])

其他类似list的操作方法

from collections import deque
d = deque(['a','b','c','d','e','f'])
print(d)
print(len(d))#获取双端队列的长度
print(d[-1])#类似list的索引访问,使用方法与列表的索引一样
# d.reverse()
# print(d)#反转输出
print(d.count('a'))#输出元素出现的次数
print(d.index('e'))
print(d.index('c',0,3))  #指定查找区间
#插入元素
d.insert(0,1)
print(d)
#remove(value)
#删除指定元素
d.remove('a')
print(d)

deque(['a', 'b', 'c', 'd', 'e', 'f'])
6
f
1
4
2
deque([1, 'a', 'b', 'c', 'd', 'e', 'f'])
deque([1, 'b', 'c', 'd', 'e', 'f'])

注意:如果使用deque进行切片的话会抛出异常

TypeError: sequence index must be integer, not 'slice',中文翻译的意思就是:类型错误:序列索引必须是整数,而不是“切片”。

from collections import deque
d = deque([1,2,3,4])
print(d[:-1])

Traceback (most recent call last):
  File "G:/Python源码/dequetest.py", line 3, in <module>
    print(d[:-1])
TypeError: sequence index must be integer, not 'slice'

rotate(把右边元素放到左边,默认为1)

from collections import deque
d = deque([1,2,3,4,5])
d.rotate()#默认为1
d.rotate(1)
print(d)

deque([5, 1, 2, 3, 4])

参数>0的时候是从右往左放元素

from collections import deque
d = deque([1,2,3,4,5])
d.rotate(2)
print(d)

deque([4, 5, 1, 2, 3])

参数<0的时候是从左往右放元素

from collections import deque
d = deque([1,2,3,4,5])
d.rotate(-2)
print(d)

deque([3, 4, 5, 1, 2])

还有一个值得关注的地方,初始化deque的时候可以给他传一个参数maxlen,如果deque中的元素超过maxlen的值,那么就会从deque中的一边去删除元素,也就是deque始终保持maxlen最大长度的元素,如果超过了就会自动把以前的元素弹出(删除)。当然这些追加都是没有问题的。

from collections import deque
d = deque([1,2,3],maxlen = 3)
print(d)
d.append(4)
print(d)
d.appendleft(5)
print(d)

deque([1, 2, 3], maxlen=3)
deque([2, 3, 4], maxlen=3)
deque([5, 2, 3], maxlen=3)

当时如果我使用insert(index,value)就会抛出异常。当然这种情况出现在我队列中的元素==maxlen的情况下使用insert才会抛出异常。如果元素!=maxlen的时候insert没有问题。我觉得可能在指定位置插入的话,他不知道去删除那一端的元素。

from collections import deque
d = deque([1,2,3],maxlen = 3)
# d.insert(0,'a')
# print(d)
d.insert(1,'b')
print(d)

Traceback (most recent call last):
  File "G:/Python源码/collections_deque.py", line 5, in <module>
    d.insert(1,'b')
IndexError: deque already at its maximum size

当然删除操作肯定不会有什么问题。

from collections import deque
d = deque([1,2,3],maxlen = 3)
print(d)
d.pop()
print(d)
d.popleft()
print(d)
d.remove(2)
print(d)

deque([1, 2, 3], maxlen=3)
deque([1, 2], maxlen=3)
deque([2], maxlen=3)
deque([], maxlen=3)