Menu
CoddyTech

队列

最后更新

队列有两个活动的端口。新值从队尾加入,值从队头离开,所以等得最久的那个最先被处理。这就是先进先出(FIFO),和柜台前排队完全一样:从队尾加入、从队头接受服务,等待才公平。点击上方的“播放”,观看值从一侧进入、从另一侧离开。

因为两端各有自己的下标或指针,两种操作都是 O(1),都不需要移动其余数据。所以凡是按到达顺序处理任务的地方,底层都有队列:打印任务、任务队列和消息队列、请求缓冲区,还有广度优先搜索,它正是因为把待访问的边界存放在队列里,才能逐层访问图。把删除的一端移到队尾,你得到的就是栈。

时间复杂度与空间复杂度

以下是基于环形缓冲区或链表实现的队列,这是两种标准实现方式:

操作复杂度说明
入队(enqueue)O(1)在队尾写入,并把队尾下标向前移动。
出队(dequeue)O(1)在队头读取,并把队头下标向前移动,不需要移动元素。
查看队头(peek)O(1)读取队头的值,但不删除它。
查找O(n)这不是队列的用途:要看到里面的内容,必须把它清空。
空间O(n)每个等待中的值占一个位置。

分步说明

步骤发生了什么
1队列一开始是空的,队头和队尾指向同一个位置。
2入队时把值写到队尾,然后队尾向前移动一位。
3之后每次入队,新值都排在已经在等待的值后面。
4出队时读取队头的值,然后队头向前移动一位。
5取出的值永远是等待时间最长的那个。
6当队头追上队尾时,队列再次为空,这时继续出队就会出错。

实例演示

依次让 3、7、5 入队,然后把队列清空:

操作队列(从队头到队尾)返回值
enqueue(3)[3]无
enqueue(7)[3, 7]无
enqueue(5)[3, 7, 5]无
dequeue()[7, 5]3,最早加入的值
dequeue()[5]7
dequeue()[]5,最新加入的值,最后取出

什么时候用队列

适合使用不适合使用
任务必须按到达顺序处理:任务队列、请求缓冲区、打印队列你需要先取最新的元素,这时该用栈
你要逐层探索,就像广度优先搜索那样元素要按优先级而不是到达顺序处理,这时适合用堆
生产者和消费者速度不同,需要在两者之间放一个缓冲区你需要在数据中间查找或按下标访问
你想要 O(1) 的插入和删除,并且不移动元素你打算每次出队都移动整个数组来实现它,这会让出队变成 O(n)

Queue 代码

简洁、可运行的 Queue 实现,提供 Python, JavaScript, Java, C++, C 版本。选择一种语言,复制代码,或在 Coddy 在线编辑器中直接打开。

Queue 的 Python 代码

Python
1from collections import deque2
3queue = deque()4
5# Enqueue three values at the rear6for value in [3, 7, 5]:7    queue.append(value)8    print(f"enqueue {value} -> {list(queue)}")9
10# Dequeue them from the front: first in, first out11while queue:12    value = queue.popleft()13    print(f"dequeue {value} -> {list(queue)}")14
15print("empty:", len(queue) == 0)
在 Python 在线编辑器中运行这段代码

队列常见问题

FIFO 是什么意思?
先进先出(First In, First Out):等待最久的值最先被处理。售票窗口前的队伍就是日常生活中的例子。栈则遵循相反的规则,即后进先出(LIFO)。
队列和栈有什么区别?
区别只在于从哪一端删除。两者都以 O(1) 在末尾添加;队列从队头删除(FIFO),栈从添加的同一端删除(LIFO)。除此之外,它们的复杂度表完全相同。
队列有哪些主要操作?
enqueue 在队尾添加一个值,dequeue 删除并返回队头的值,peek(或 front)读取队头但不删除,is_empty 判断是否还有元素在等待。这四个操作都是 O(1)。
为什么用普通数组实现时出队很慢?
因为从数组中删除下标 0 的元素会让其余所有元素向左移动,每次出队都变成 O(n)。实际的实现会用移动队头下标的环形缓冲区,或者带头指针的链表来避免这个问题。Python 的 collections.deque 和 Java 的 ArrayDeque 已经替你做好了,而 list.pop(0) 没有。
循环队列是什么?
循环队列建立在固定大小的数组上,队头和队尾下标走到末尾时会绕回 0。它会重复利用出队后空出来的位置,所以容量为 n 的队列可以一直工作下去,而不会走出数组的末尾。
实际程序中哪里会用到队列?
服务之间的任务队列和消息队列、打印和作业排队、Web 服务器中的请求缓冲区、键盘和事件缓冲区、生产者消费者管道,还有广度优先搜索,正是队列让它能够逐层遍历。
Coddy 编程语言插图

在 Coddy 掌握算法

开始