Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

队列与双端队列

复习

  • 栈:后进先出,只在一端操作
  • 单向链表:节点用指针相连
  • 抽象数据类型:接口与实现分离

TL;DR

  • 队列是“先进先出”的结构
  • 一端入队,另一端出队
  • 它契合调度、缓冲等场景
  • 双端队列两端都能进能出,更灵活

正文

  栈是“后来者先走”。可很多事情恰恰相反:先来的应该先得到处理。这种“先来先服务”的顺序,对应的结构就是队列(queue)。

先进先出

  队列的规则是先进先出(FIFO,First In First Out)。

  它就像现实中的排队:从队尾加入,从队首离开,先来的人先被服务,不会有人插队。

  核心操作有两个:

  • 入队(enqueue):从队尾加入元素
  • 出队(dequeue):从队首取出元素

  它可以用链表实现(尾进头出),也可以用数组加两个指针实现(用循环的方式避免浪费前面的空间)。基本操作也都是 O(1)

它适合什么

  队列的用武之地,几乎都和“先来先服务”“缓冲”有关:

  • 打印任务:谁先提交谁先打印
  • 任务调度:按到达顺序依次处理
  • 缓冲区:数据从一个环节流向下一个环节,先到的先被取走
  • 广度优先搜索:一层层向外扩展时,正好用队列维护“待处理的节点”

  最后一条尤其重要——它把队列和后面的图遍历联系了起来。一个结构用在哪里,往往由它天然的顺序语义决定。

两端都灵活的:双端队列

  双端队列(deque,double-ended queue)是队列的加强版:两端都能进、都能出

  它同时具备栈和队列的能力:

  • 只用一端进出,它就像栈
  • 一端进、另一端出,它就像队列

  所以双端队列常被当成一个“万能”的线性容器,需要哪种顺序就用哪种。当然,能力更全,实现和维护也稍复杂一点。

  不过,队列有个隐含假设:先到的先被处理。 可如果有些任务更紧急,应该优先处理呢?那就不能只靠“先来后到”了。下一章开始,我们要给元素加上“优先级”,由此进入树与堆的世界。

思考题 1

  队列的“先进先出”适合哪些现实场景?

思考题 2

  双端队列与栈、队列分别是什么关系?

小结

知识点

  • 队列是先进先出的抽象数据类型
  • 一端入队、另一端出队,基本操作 O(1)
  • 队列用于调度、缓冲与广度优先搜索
  • 双端队列两端都可进出,兼具栈与队列能力

参考资料

  1. Wikipedia(zh):队列:先进先出的数据结构
  2. Wikipedia(zh):双端队列:两端都可插入删除的队列

思考题答案(仅供参考)

思考题 1

  适合“先来先服务”和“缓冲”类的场景,比如打印任务、任务调度、数据缓冲区、以及广度优先搜索中的待处理节点维护。这些场景都要求按到达顺序依次处理,正好符合先进先出。

思考题 2

  只用一端插入和删除时,双端队列的行为就是栈;一端插入、另一端删除时,它就是队列。所以双端队列是两者的加强版,能力更全,可以按需当作栈或队列使用。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪