二叉树宽度是各层节点数的最大值,需用BFS层序遍历:每层前保存队列大小作为当前层节点数,for循环内只处理该层节点并更新最大值;空树返回0,避免空指针入队和动态调用size。

用 BFS 层序遍历统计每层节点数
二叉树宽度指某一层中节点数量的最大值,不是左右最远距离。最直接可靠的做法是用广度优先搜索(BFS)逐层处理,每轮记录当前队列大小——即该层节点数。std::queue 是标准选择,配合 TreeNode* 指针操作即可。
常见错误是误把「深度」当「宽度」,或在递归中试图维护全局最大宽度但没正确区分层级;还有人用 DFS 加 depth 映射计数,但容易因插入顺序或哈希遍历不确定性导致逻辑混乱。
- 每次进入 while 循环前,先保存
queue.size(),这就是当前层节点总数 - 内层 for 循环执行
size次,保证只处理本层节点,子节点统一推入队尾 - 更新全局最大值:
max_width = std::max(max_width, current_level_size) - 空树要特判,
root == nullptr时直接返回 0
为什么不用 DFS + map 记录 depth 频次
虽然可以用 std::map<int int></int> 或 std::unordered_map 在 DFS 中按 depth 累加节点数,但实际引入了额外复杂度和隐患:
- DFS 本身不保证同一 depth 的访问顺序,但频次统计本身没问题;真正问题是:若树极深(比如退化成链),递归可能导致栈溢出,而 BFS 的队列至少可控(可用迭代+显式栈模拟)
-
std::map插入是O(log d),d 为不同深度数,虽小但无必要;unordered_map哈希冲突可能影响常数性能 - 更隐蔽的坑:如果用 vector 索引 depth(如
count[depth]++),必须确保depth不越界,而树深度未知,容易触发std::vector::operator[]的未定义行为
C++ 实现要点与边界检查
假设节点结构体为典型形式:
立即学习“C++免费学习笔记(深入)”;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
核心逻辑简洁,但几个细节决定健壮性:
- 别忘了包含头文件:
#include <queue>、#include <algorithm>、#include <climits>(如果用INT_MIN初始化) - 初始化
max_width为 0,而非 1——空树必须返回 0 - 队列 push 前检查
root != nullptr,否则空指针进队列会导致后续解引用崩溃 - 循环内不要在 for 中反复调用
q.size(),它会随 push 动态变化;务必在 for 外存快照
一个最小可运行示例
以下代码可直接编译运行,覆盖空树、单节点、两层不平衡树等场景:
int widthOfBinaryTree(TreeNode* root) {
if (!root) return 0;
std::queue<TreeNode*> q;
q.push(root);
int max_width = 0;
while (!q.empty()) {
int level_size = q.size(); // 快照当前层节点数
max_width = std::max(max_width, level_size);
for (int i = 0; i < level_size; ++i) {
TreeNode* node = q.front(); q.pop();
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
}
return max_width;
}
注意:这个版本计算的是「层宽最大值」,不是 LeetCode 662 那种带空位编号的宽度定义。后者需记录位置索引,属于另一问题,容易混淆。
真正在意性能且树极大时,std::queue 底层 deque 默认分配策略可能引发多次内存重分配;若实测成为瓶颈,可考虑预分配空间或换用 std::vector 手动模拟双端队列,但绝大多数场景没必要。


















