层序遍历必须用队列,标准解法是每次循环前用queue.size()获取当前层节点数并一次性保存,再遍历该数量节点;错误做法是在push子节点后读size或在for内调用size,会导致下层节点混入当前层统计。

层序遍历必须用队列,不能用递归或栈
递归天然适合深度优先,强行用它做层序只会绕弯子、易出错;栈(哪怕双栈)模拟层序不仅逻辑复杂,还容易在边界(比如空树、单节点)上漏判。标准解法就是 std::queue,每次处理完当前层所有节点再推进下一层。
关键不是“能不能”,而是“要不要多维护一个计数器”——queue.size() 就是当前层节点数,每次循环前保存它,就能精准切分层级:
std::queue<TreeNode*> q;
if (root) q.push(root);
while (!q.empty()) {
int level_size = q.size(); // 当前层节点总数
std::vector<int> level_nodes;
for (int i = 0; i < level_size; ++i) {
auto node = q.front(); q.pop();
level_nodes.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
// level_nodes 就是第 N 层的全部值
}
统计每层节点数时,别在 push 后立刻读 size
常见错误是把 q.size() 放在 for 循环内部,或者在 push 子节点后才取 size——这会导致下一层节点被误算进当前层。必须在 for 循环开始前一次性捕获当前队列长度。
- ✅ 正确:先
int n = q.size(),再for (int i = 0; i - ❌ 错误:写成
for (int i = 0; i —— 每次判断都重新计算,且 push 后 size 变化 - ❌ 错误:在
q.push(node->left)之后再取q.size(),已混入下层节点
空节点不参与统计,但影响子树结构判断
题目要求“按层分级统计节点分布”,默认指非空节点。但如果你实际要还原层序序列(比如力扣 102 题),空节点通常不入队,所以 level_nodes 长度就是该层真实节点数。
立即学习“C++免费学习笔记(深入)”;
如果需要包含空占位(如完全二叉树补空),就得改用 BFS + 显式空节点入队,此时 level_nodes 中会出现 nullptr 或特殊标记,统计逻辑需额外过滤:
// 仅当明确需要补空时才启用 if (node->left) q.push(node->left); else q.push(nullptr); if (node->right) q.push(node->right); else q.push(nullptr);
这种写法会让层宽统计失真(空节点也被计入 q.size()),除非你专门区分统计逻辑。
性能敏感场景下,避免 vector 反复扩容
如果树很深、每层节点数已知上限(比如满二叉树),可提前 reserve:level_nodes.reserve(level_size)。否则 vector 默认扩容策略可能触发多次内存重分配。
更轻量的替代方案是直接用两个 vector 轮换(current / next),省去清空和重新分配开销,适合高频调用或嵌入式环境:
std::vector<TreeNode*> curr, next;
if (root) curr.push_back(root);
while (!curr.empty()) {
std::vector<int> vals;
vals.reserve(curr.size());
for (auto node : curr) {
vals.push_back(node->val);
if (node->left) next.push_back(node->left);
if (node->right) next.push_back(node->right);
}
// use vals...
curr.swap(next);
next.clear();
}
注意 next.clear() 不释放内存,后续复用更快;若需彻底释放,用 next = {} 或 next.shrink_to_fit()。


















