完全二叉树是指除最后一层外其余各层节点数均达最大,且最后一层节点从左到右连续排列、中间无空缺的二叉树;不能仅凭左右子树高度差≤1判断,因那是AVL平衡条件,而完全二叉树的核心判定依据是层序编号连续性——根编号为1,左、右孩子分别为2i和2i+1,若总节点数为n,则编号必须恰好为1至n且无缺失。

什么是完全二叉树,以及为什么不能只看高度差
完全二叉树不是“左右子树高度差 ≤ 1”——那是 AVL 树的平衡条件。真正的判断依据是:按层序编号(根为 1,左孩子为 2×i,右孩子为 2×i+1),若树中最大编号为 n,则所有编号 1 到 n 的节点都必须存在(即中间不能有空缺)。这意味着:一旦某层出现空节点,其右侧及后续所有位置都不能有非空节点。
常见错误是仅用递归检查“是否满二叉树”或“是否平衡”,结果把 [1,2,3,null,4](层序表示)误判为完全二叉树——其实它不是,因为编号 4 对应左子节点存在,但编号 5(右子节点)缺失,而编号 6(2×3)却出现了 4(它实际挂在 2 的右子,编号应为 5),编号映射已错乱。
用 BFS 层序遍历 + 编号验证最可靠
核心思路:在 BFS 过程中给每个节点打上理论编号(从 1 开始),同时记录当前遇到的最大编号 max_id;遍历结束后,若节点总数 count == max_id,说明编号连续无空洞,即为完全二叉树。
实操要点:
立即学习“C++免费学习笔记(深入)”;
- 用
queue<pair<TreeNode*, long long>>存节点和其理论编号,避免 int 溢出(深度 > 31 时编号超INT_MAX) - 遇到空节点不入队,但要继续推进队列——关键点:**一旦弹出空节点,后续所有节点都必须为空**,否则直接返回 false
- 更稳妥的做法是:BFS 中只要遇到第一个
null,就跳出循环,然后检查队列剩余元素是否全为null
示例逻辑片段:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
bool isCompleteTree(TreeNode* root) {
if (!root) return true;
queue<TreeNode*> q;
q.push(root);
bool seen_null = false;
while (!q.empty()) {
TreeNode* node = q.front(); q.pop();
if (!node) {
seen_null = true;
continue;
}
if (seen_null) return false; // 非空节点出现在空节点之后
q.push(node->left);
q.push(node->right);
}
return true;
}DFS 也能做,但容易漏掉层间空洞
DFS 天然不适合处理“层内连续性”,强行做需额外维护节点总数和每个节点的预期编号,代码易错且难调试。比如用 DFS 收集所有节点指针再排序编号,不仅空间翻倍,还依赖严格层序访问顺序(递归 DFS 不保证这点)。
除非你明确需要同时求节点数和最大编号(如用于堆数组映射验证),否则不推荐 DFS 方案。真实项目中,99% 的完全性检查场景都用 BFS 更稳。
一个典型坑:if (node->left) dfs(node->left, 2*id) —— 若树稀疏,2*id 可能远超实际节点数,导致数组越界或误判;且无法感知“同一层中左空右非空”这种非法结构。
LeetCode 958 验证用例与边界注意点
isCompleteTree 在 LeetCode 958 题中要求严格符合定义。几个关键边界:
- 单节点树
[1]→ true - 只有左孩子的树
[1,2]→ true(编号 1,2 连续) - 只有右孩子的树
[1,null,2]→ false(编号 2 缺失,3 却存在) - 深层空洞:
[1,2,3,null,null,4,5]→ false(第 2 层 4、5 存在,但第 2 层左子树的右子(编号 5)本该在第 3 层,实际却和编号 6 的节点同层,破坏编号连续性)
测试时别忘了空树输入:nullptr 应返回 true(空树视为平凡满足)。
真正容易被忽略的是编号溢出和空节点后的非空判定顺序——这两个点一错,整道题过不了全部用例。

















