首页
/ tech-interview-handbook 队列(Queue)算法面试指南:数据结构、时间复杂度、常见陷阱与练习路线

tech-interview-handbook 队列(Queue)算法面试指南:数据结构、时间复杂度、常见陷阱与练习路线

2026-09-04 11:26:17作者:吴年前Myrtle

本文基于 tech-interview-handbook 的 Queue 算法备忘单 展开,系统讲解队列的 FIFO 语义、各语言标准库实现、O(1) 操作的时间复杂度依据、面试中最容易被追问的"用数组模拟队列导致 O(n) 出队"陷阱,以及仓库配套的 BFS、拓扑排序实战用法与练习题目路线。读完后你能在编码面试中正确选型队列实现、预判其复杂度边界,并掌握 LeetCode 上围绕队列的核心必刷题型。

队列的数据结构与 FIFO 语义

队列(Queue)是一种线性元素集合,元素按顺序维护,并只允许两端操作:

  • 入队(enqueue):在序列的一端添加元素;
  • 出队(dequeue):从序列的另一端移除元素。

按惯例,添加元素的一端称为队列的尾部(back、tail 或 rear),移除元素的一端称为头部(head 或 front)。这种"先进先出"的行为即 FIFO(First In, First Out)——"queue"这一命名本身就来自现实中人们排队等候商品或服务的类比。

作为抽象数据类型,队列可以用数组单向链表实现。理解这一点对面试很重要:数组实现出队需要整体左移元素,链表实现则通过移动头指针完成,两者的复杂度特性差异正是面试中高频的讨论点(后文"面试中要注意的点"一节会展开)。

队列最典型的应用场景是广度优先搜索(BFS):BFS 用队列保存"已遇到但尚未探索"的节点,保证按层次逐层扩展。仓库的 Graph 算法备忘单 中专门说明了这一点,并给出了基于 deque 的 BFS 模板(见下文"仓库中的实战用法")。

在 handbook 学习路线中的位置

coding interview study plan 的四周计划中,Queue 被安排在第 2 周,优先级为 Mid(中),建议投入 2 小时

主题 优先级 所需时间
Queue Mid 2 hours

同周与它并列的还有 Sorting and searching(High)、Matrix(High)、Linked List(Mid)和 Stack(Mid)。这意味着队列不需要像 Array/Tree 那样投入大量时间,但它是第 3 周学习 Graph、Heap 之前必须打好的基础数据结构——BFS 模板直接依赖队列,拓扑排序的 Kahn 算法同样以队列为核心。

各语言的标准库实现

原文档给出了各语言应使用的队列 API,面试手写代码前应先确认这些工具,避免重复造轮子:

语言 推荐 API
C++ std::queue
Java java.util.Queue 接口;实现上建议用 java.util.ArrayDeque(文档明确建议"Use java.util.ArrayDeque")
Python collections.deque
JavaScript N/A(没有内置队列类,需自行用数组模拟或使用第三方数据结构库)

几个面试中值得注意的细节:

  • Java 的 ArrayDeque 优于 LinkedList:两者都实现了 Queue 接口,但 ArrayDeque 基于循环数组,缓存友好且无节点对象开销,官方文档也不建议将 LinkedList 用作队列/栈;
  • Python 的 deque 是双向链表:两端 append/popleft 都是 O(1),同时可当栈使用。仓库 Graph 备忘单的 BFS 部分 就指出"deque class in Python can function as both a stack and a queue";
  • JavaScript 没有内置 Queue:这正是后文陷阱的根源——JS 面试中用数组模拟队列时,必须主动管理头部索引,否则每次出队都是 O(n)。

时间复杂度

对于标准库实现(C++ std::queue、Java ArrayDeque、Python deque),核心操作的复杂度如下:

操作 复杂度
Enqueue / Offer O(1)
Dequeue / Poll O(1)
Front O(1)
Back O(1)
isEmpty O(1)

注意这套 O(1) 结论只对真正的队列实现成立,对"用数组/Python list 冒充队列"不成立——这是下面这一节要讲的核心陷阱。

面试中要注意的点:模拟队列的 O(n) 出队问题

原文档专门列出的面试注意事项,也是本主题最值得内化的一条:

大多数语言没有可直接使用的内置 Queue 类,候选人经常用数组(JavaScript)或列表(Python)充当队列。但在这种场景下,出队操作(假设队列头部在左侧)是 O(n) 的,因为它要求把所有元素整体向左移动一位。遇到这种情况,你可以主动提示面试官:你假设存在一个出队操作高效的队列数据结构可用。

这条建议包含两层实战价值:

  1. 复杂度判断能力:面试官考察的往往不是你写不写得出 O(n) 版出队,而是你是否意识到它慢在哪。Python 里 list.pop(0) 需要 O(n) 搬移,list.append() 却是 O(1) 均摊——用列表做队列时,只有"尾部进出"是快的;
  2. 主动沟通(flagging assumptions):明确向面试官声明"我假设可以 O(1) 出队",是 handbook 在编码面试技巧中一贯提倡的行为——把隐含假设说破,既展示知识边界,也避免被复杂度漏洞扣分。

仓库中 Graph 备忘单 对这一点有呼应,在 BFS 模板处特别强调:

"It is important to use double-ended queues and not arrays/Python lists as dequeuing for double-ended queues is O(1) but it's O(n) for arrays."

(必须用双向队列而不能用数组/Python list,因为双向队列出队是 O(1),而数组是 O(n)。)

仓库中的实战用法:BFS 与拓扑排序

BFS 模板(矩阵版)

apps/website/contents/algorithms/graph.md 给出的 BFS 模板完整展示了队列在算法题中的标准用法——入队(append)、出队(popleft)、判空(while queue)三个 O(1) 操作贯穿全程:

from collections import deque

def bfs(matrix):
  # Check for an empty matrix/graph.
  if not matrix:
    return []

  rows, cols = len(matrix), len(matrix[0])
  visited = set()
  directions = ((0, 1), (0, -1), (1, 0), (-1, 0))

  def traverse(i, j):
    queue = deque([(i, j)])
    while queue:
      curr_i, curr_j = queue.popleft()
      if (curr_i, curr_j) not in visited:
        visited.add((curr_i, curr_j))
        # Traverse neighbors.
        for direction in directions:
          next_i, next_j = curr_i + direction[0], curr_j + direction[1]
          if 0 <= next_i < rows and 0 <= next_j < cols:
            # Add in question-specific checks, where relevant.
            queue.append((next_i, next_j))

  for i in range(rows):
    for j in range(cols):
      traverse(i, j)

同文件中还给出了矩阵 DFS 递归模板(见 graph_dfs.py 的独立可运行版本),并指出:DFS 与 BFS 的本质区别就在于底层数据结构——"BFS uses a queue while DFS uses a stack",同一个 Python deque 换个调用方向(append/pop 代替 append/popleft)即可互换角色。

拓扑排序(Kahn 算法)

队列的另一大经典应用是拓扑排序:把入度为 0 的节点入队,出队时削减其后继节点入度,新变为 0 的节点再入队。graph_topo_sort.py 是一个完整的独立可运行实现,Graph 备忘单 也内嵌了同样逻辑:

def graph_topo_sort(num_nodes, edges):
    from collections import deque
    nodes, order, queue = {}, [], deque()
    for node_id in range(num_nodes):
        nodes[node_id] = { 'in': 0, 'out': set() }
    for node_id, pre_id in edges:
        nodes[node_id]['in'] += 1
        nodes[pre_id]['out'].add(node_id)
    for node_id in nodes.keys():
        if nodes[node_id]['in'] == 0:
            queue.append(node_id)
    while len(queue):
        node_id = queue.popleft()
        for outgoing_id in nodes[node_id]['out']:
            nodes[outgoing_id]['in'] -= 1
            if nodes[outgoing_id]['in'] == 0:
                queue.append(outgoing_id)
        order.append(node_id)
    return order if len(order) == num_nodes else None

典型应用背景:带依赖关系的任务调度、大学选课(先修课程)等——任务建模为顶点,若任务 x 必须先于 y 完成则存在从 x 到 y 的边。

边界情况(Corner cases)

原文档列出的队列题必查边界条件,面试写代码前应主动覆盖:

  • 空队列:对空队列执行 dequeue/front 应如何返回(抛异常、返回哨兵值、或先 isEmpty 判断);
  • 只有一个元素的队列:出队后直接变空,验证"出队→判空"的顺序是否正确;
  • 有两个元素的队列:验证连续出队的顺序严格保持 FIFO,以及入队/出队交错时的相对顺序。

对于"实现队列"类题目(见下节),这三类输入正好构成测试用例骨架,尤其空队列是绝大多数实现出错的地方(例如在 size 为 0 时访问 back()/front())。

必刷题与推荐练习

Essential question(核心必刷)

原文档标注" studying 这个主题时必练"的题目:

  • Implement Stack using Queues(用队列实现栈):要求只用队列的基本操作实现 pushpoptopisEmpty,且 pop/top 均摊 O(1) 的常见做法是维护两个队列,只让一个队列保持"只有一个元素"的状态。它是"队列 ↔ 栈互换模拟"题型的双胞胎,做它之前可以先想清楚两种结构在弹出顺序(LIFO vs FIFO)上的本质差异。

值得一提的是,QuestionGroups.json 的练习题库(Week 2 分组)中收录了 Implement Queue using Stacks(LeetCode 232,Easy,预计 20 分钟),它属于 stack 主题但与本题互为镜像——两道一起刷能同时巩固两个方向。

Recommended practice questions(进阶练习)

学完主题并做完必刷题后,原文档推荐的练习题目:

  • Implement Queue using Stacks(用栈实现队列):用两个栈实现 FIFO,push O(1),pop 均摊 O(1);
  • Design Circular Queue(设计环形队列):定长数组 + head/tail 指针 + 模运算的经典设计题,直接呼应本文"数组实现队列"的底层原理——环形索引正是解决数组实现"整体左移 O(n)"问题的标准答案;
  • Design Hit Counter(LeetCode Premium):用滑动时间窗口 + 队列(或有序结构)统计命中次数,考察队列在"只关心最旧元素何时过期"场景下的运用。

学习资源与课程

原文档 Learning resources 部分推荐的外部资源(按原文保留条目名称,链接见仓库原文档):

  • Readings:basecs 的《To Queue Or Not To Queue》——从"排队"类比切入讲解队列的文章;
  • Videos:加州大学圣地亚哥分校(UC San Diego)Coursera 数据结构课程的 Queues 一课。

文档末尾通过 AlgorithmCourses 组件引入的推荐课程包括:AlgoMonster(一次性买断、Google 工程师出题模式)、Design Gurus 的 Grokking the Coding Interview(按题目模式而非单题记忆组织练习)、Udemy 的 Master the Coding Interview: Data Structures + Algorithms(综合覆盖简历、非技术面试与薪酬谈判)。

小结

把 Queue 这一个主题压缩成面试可用的检查清单:

  1. 定义:FIFO 线性结构,尾部入队、头部出队,底层可用数组或单向链表实现;
  2. 选型:C++ std::queue、Java ArrayDeque(实现 Queue 接口)、Python collections.deque、JavaScript 无内置实现;
  3. 复杂度:标准库实现下 Enqueue/Dequeue/Front/Back/isEmpty 全部 O(1);
  4. 陷阱:用普通数组/Python list 模拟队列时出队是 O(n),要在面试中主动声明假设或改用双端队列/环形索引;
  5. 边界:空队列、单元素、双元素三种情况必须覆盖;
  6. 应用:BFS 逐层扩展、Kahn 拓扑排序、滑动窗口过期淘汰(Hit Counter);
  7. 练习:先刷 Implement Stack using Queues,再做 Implement Queue using Stacks、Design Circular Queue、Design Hit Counter,总投入按 study plan 约 2 小时即可达标。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384