因为bfs需“先进先出”,queue原生支持o(1)入队/出队且语义明确;list模拟出队为o(n),stack则变成dfs;图遍历时须用hashset判重,避免死循环。

为什么用 Queue<t></t> 而不是其他集合
因为 BFS 的核心是“先进先出”,必须按层顺序访问节点,Queue<t></t> 是 .NET 中唯一原生支持 O(1) 入队/出队且语义明确的集合。用 List<t></t> 模拟(比如 RemoveAt(0))会导致每次出队都 O(n) 移动元素,树稍深就明显卡顿;用 Stack<t></t> 则直接变成 DFS,逻辑错位。
Queue<t></t> 实现 BFS 的关键三步
以二叉树为例,实际写法极简,但每步都有明确意图:
- 初始化:把根节点入队 ——
queue.Enqueue(root) - 循环处理:只要队列非空,就
Dequeue()取出当前层第一个节点,访问它,再把它的子节点(左、右)依次Enqueue() - 终止条件:队列为空,说明所有可达节点已遍历完毕
示例片段(无异常处理,聚焦主干):
var queue = new Queue<TreeNode>(); if (root != null) queue.Enqueue(root); while (queue.count > 0) { var node = queue.Dequeue(); Console.WriteLine(node.val); // 访问 if (node.left != null) queue.Enqueue(node.left); if (node.right != null) queue.Enqueue(node.right); }
图结构中 BFS 必须加 visited 集合
树天然无环,但图可能有环或重复边,不判重会导致死循环。常见错误是只靠队列去重 —— Queue 不提供 Contains 高效查询,且入队前未检查是否已入过队。
- 正确做法:用
HashSet<t></t>记录已访问节点,每次入队前先查visited.Contains(neighbor) - 注意:
HashSet的Add()返回bool,可直接用于跳过已访问节点:if (visited.Add(neighbor)) queue.Enqueue(neighbor); - 不要用
List<t>.Contains()</t>替代,大数据量下性能断崖式下跌
BFS 中记录层数或距离的惯用写法
很多题目要求返回最短路径长度、最小操作步数等,不能只靠节点值,得知道当前在第几层。
- 方法一(推荐):每轮 while 循环代表一层,用内层 for 循环固定处理当前队列长度个节点 ——
int size = queue.Count; for (int i = 0; i - 方法二:在入队时同时存节点和对应层数,用元组或自定义类,如
Queue - 避免在节点类里加临时字段存 level,污染数据结构,且多线程不安全
层级计数容易漏掉初始层判断,比如从起点出发算第 0 层还是第 1 层,需严格对照题意。