std::queue实现BFS层序遍历的核心是利用FIFO特性,每层前用q.size()冻结节点数,循环pop并压入非空子节点,避免混层;须判空树、存指针、不入nullptr。

用 std::queue 实现 BFS 层序遍历最直接
核心就是借助队列的 FIFO 特性,逐层把节点压入、弹出、访问子节点。C++ 标准库的 std::queue 足够可靠,无需手写队列结构。
关键点在于:每次处理一层前,先记录当前队列长度(即本层节点数),然后循环 pop 这么多次,对每个弹出节点把它的 left 和 right 指针非空时压入队列。这样能天然隔离层级。
常见错误是直接在循环中边 pop 边 push,却不固定本层大小,导致下层节点混入本轮遍历 —— 结果变成“看似 BFS,实为乱序”。
- 必须在每层开始前用
int levelSize = q.size()冻结当前层宽度 - 不要用
while (!q.empty())套一层就完事,那样无法分层输出或处理 - 空树要特判:
if (!root) return;,否则对空指针取->left会崩溃
如何按层返回二维 vector(如 LeetCode 102)
典型需求是返回 vector<vector>></vector>,每内层 vector 对应一层节点值。这要求你在每次处理完一层后,把该层所有值塞进一个临时 vector<int></int>,再 push_back 到结果中。
立即学习“C++免费学习笔记(深入)”;
注意别在内层循环里反复调用 result.push_back({val}) —— 那样每节点建一个 vector,效率低且结构错乱。
示例片段:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if (!root) return res;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int n = q.size();
vector<int> level;
for (int i = 0; i < n; ++i) {
TreeNode* node = q.front(); q.pop();
level.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
res.push_back(level); // ← 整层 push,不是单个节点
}
return res;
}
queue 存指针还是存值?
必须存 TreeNode*(或智能指针如 shared_ptr<TreeNode>),不能存 TreeNode 本身。
原因很实在:二叉树节点通常较大(含指针成员),拷贝开销高;更重要的是,拷贝后 left/right 指向的是原节点的副本,而非原始树结构,后续遍历会断链甚至访问野指针。
- 存
TreeNode*是零拷贝、语义清晰、符合实际使用场景 - 如果担心裸指针生命周期,可用
queue<shared_ptr<TreeNode>>,但需确保建树时也用make_shared - 绝对避免
queue<TreeNode>—— 编译可能过,运行必错
遇到空节点怎么跳过(比如 LeetCode 107 反向层序)
空节点本身不入队,所以“跳过”是默认行为。真正要注意的是:当你需要补空值(例如输出 [1,null,2,3] 这类带 null 的数组),就不能依赖 std::queue —— 因为它不保存空位信息。
此时得换思路:用 vector 模拟完全二叉树索引(类似堆),或改用双端队列 + 占位标记。但绝大多数真实 BFS 场景(包括判断是否满/完满二叉树、找最左节点等),都只关心非空节点,空指针直接忽略即可。
一句话:只要不显式要求输出 null,就保持 if (node->left) 和 if (node->right) 这种守门逻辑,干净又高效。
层级边界和空指针处理,是 BFS 实现里最容易被当成“理所当然”而漏掉验证的两个点。


















