二叉树的完全性检验 图解题解
这道题到底在问什么
- 输入
- [1,2,3,4,5,6]
- 输出
- true(最后一层从左填满)
- 输入
- [1,2,3,4,5,null,7]
- 输出
- false(空位后又冒出 7)
最优解:一步一步想明白
- 3记住「空孩子也入队;见空之后再遇真节点就 false」,下面逐帧套它。
- 4根入队先看这棵树。把根 50 放进队列,见空开关 seen 还是关着。开始一个个出队。
- 5seen 仍关出队真节点 50,此刻 seen 还关着,合法。接下来把它的左、右孩子都入队,空孩子也要入队一个空位。
- 6空孩子也入队左孩子 30 入队;右孩子 80 入队。50 处理完(标绿)。把空孩子也排进队,是为了让可能的「空洞」在后面暴露出来。
- 7seen 仍关出队真节点 30,此刻 seen 还关着,合法。接下来把它的左、右孩子都入队,空孩子也要入队一个空位。
- 8空孩子也入队左孩子 20 入队;右孩子 40 入队。30 处理完(标绿)。把空孩子也排进队,是为了让可能的「空洞」在后面暴露出来。
- 9seen 仍关出队真节点 80,此刻 seen 还关着,合法。接下来把它的左、右孩子都入队,空孩子也要入队一个空位。
- 10空孩子也入队左孩子 60 入队;右孩子是空的,入队一个空位。80 处理完(标绿)。把空孩子也排进队,是为了让可能的「空洞」在后面暴露出来。
- 11seen 仍关出队真节点 20,此刻 seen 还关着,合法。接下来把它的左、右孩子都入队,空孩子也要入队一个空位。
- 12空孩子也入队左孩子是空的,入队一个空位;右孩子是空的,入队一个空位。20 处理完(标绿)。把空孩子也排进队,是为了让可能的「空洞」在后面暴露出来。
- 13seen 仍关出队真节点 40,此刻 seen 还关着,合法。接下来把它的左、右孩子都入队,空孩子也要入队一个空位。
- 14空孩子也入队左孩子是空的,入队一个空位;右孩子是空的,入队一个空位。40 处理完(标绿)。把空孩子也排进队,是为了让可能的「空洞」在后面暴露出来。
- 15seen 仍关出队真节点 60,此刻 seen 还关着,合法。接下来把它的左、右孩子都入队,空孩子也要入队一个空位。
- 16空孩子也入队左孩子是空的,入队一个空位;右孩子是空的,入队一个空位。60 处理完(标绿)。把空孩子也排进队,是为了让可能的「空洞」在后面暴露出来。
- 17seen 打开出队出到一个空位,打开见空开关 seen。再看队列里剩下的:全是空位、没有任何真节点了。空位后面没再冒出节点,完全性成立。
- 18true树 A(完全) 的结论:true(完全)。从左到右紧凑、最后一层靠左、没有空洞。
- 19根入队先看这棵树。把根 50 放进队列,见空开关 seen 还是关着。开始一个个出队。
- 20seen 仍关出队真节点 50,此刻 seen 还关着,合法。接下来把它的左、右孩子都入队,空孩子也要入队一个空位。
- 21空孩子也入队左孩子 30 入队;右孩子 80 入队。50 处理完(标绿)。把空孩子也排进队,是为了让可能的「空洞」在后面暴露出来。
- 22seen 仍关出队真节点 30,此刻 seen 还关着,合法。接下来把它的左、右孩子都入队,空孩子也要入队一个空位。
- 23空孩子也入队左孩子 20 入队;右孩子 40 入队。30 处理完(标绿)。把空孩子也排进队,是为了让可能的「空洞」在后面暴露出来。
- 24seen 仍关出队真节点 80,此刻 seen 还关着,合法。接下来把它的左、右孩子都入队,空孩子也要入队一个空位。
- 25空孩子也入队左孩子是空的,入队一个空位;右孩子 70 入队。80 处理完(标绿)。把空孩子也排进队,是为了让可能的「空洞」在后面暴露出来。
- 26seen 仍关出队真节点 20,此刻 seen 还关着,合法。接下来把它的左、右孩子都入队,空孩子也要入队一个空位。
- 27空孩子也入队左孩子是空的,入队一个空位;右孩子是空的,入队一个空位。20 处理完(标绿)。把空孩子也排进队,是为了让可能的「空洞」在后面暴露出来。
- 28seen 仍关出队真节点 40,此刻 seen 还关着,合法。接下来把它的左、右孩子都入队,空孩子也要入队一个空位。
- 29空孩子也入队左孩子是空的,入队一个空位;右孩子是空的,入队一个空位。40 处理完(标绿)。把空孩子也排进队,是为了让可能的「空洞」在后面暴露出来。
- 30seen 打开出队出到一个空位,打开见空开关 seen。注意队列后面还排着真节点,接下来只要再出到它,就违例了。
- 31判定 false见空开关已经打开,却又出队到真节点 70(标红)。这说明前面的空位后面还有节点、中间留了空洞,判 false,不是完全二叉树。
- 32false树 B(不完全) 的结论:false(不完全)。空位之后还冒出了节点,出现空洞,所以不完全。
⚠️ 容易写错的地方
✗ 错:空孩子不入队
✓ 对:左右孩子都入队,空的也入队一个空位
不把空入队,队列里就看不出「空洞」,无法判定见空后是否还有节点
✗ 错:见到空就立刻返回 false
✓ 对:见空只打开 seen 开关,继续出队
最后一层本来就允许有空位;只有「见空之后又出现真节点」才算不完全
✗ 错:用层序数组里有没有 null 来判
✓ 对:按 BFS 出队顺序判
完全性看的是出队序列里空位与真节点的先后,不是简单数 null 个数
完整代码(Python / C++ / Java)
Python
from typing import List, Optional
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def isCompleteTree(self, root: Optional[TreeNode]) -> bool:
q = deque([root])
seen_null = False
while q:
node = q.popleft()
if not node:
seen_null = True
else:
if seen_null: return False
q.append(node.left); q.append(node.right)
return TrueC++
#include <algorithm>
#include <climits>
#include <functional>
#include <queue>
#include <vector>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution { public: bool isCompleteTree(TreeNode* root){ queue<TreeNode*> q; q.push(root); bool seen=false; while(!q.empty()){ auto n=q.front(); q.pop(); if(!n) seen=true; else { if(seen) return false; q.push(n->left); q.push(n->right);} } return true; } };Java
import java.util.*;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; }
}
class Solution { public boolean isCompleteTree(TreeNode root){ Queue<TreeNode> q=new LinkedList<>(); q.offer(root); boolean seen=false; while(!q.isEmpty()){ TreeNode n=q.poll(); if(n==null) seen=true; else { if(seen)return false; q.offer(n.left); q.offer(n.right); } } return true; } }复杂度
时间
O(n)
每个真节点入队、出队各一次,连同它们的空孩子占位也是常数倍,总共 O(n)
空间
O(w)
队列里最多同时装一层的节点(含空占位),w 是树的最大宽度,最坏可到 O(n)
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 二叉树的完全性检验 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
能不能用「按下标编号」的方法做?+
可以。给根编号 1,节点 i 的左孩子编号 2i、右孩子 2i+1。BFS 走一遍,记录最大编号 maxId,并统计节点数 n;当且仅当 maxId 等于 n 时是完全二叉树(编号没有跳号、没有空洞)。和「空孩子入队 + 见空开关」是两套等价判法。
为什么最后一层有空位也可能是完全的?+
完全二叉树的定义就允许最后一层不填满,只要这些节点都紧靠左边、右侧连续为空即可。所以判据不是「有没有空」,而是「第一个空位之后还有没有真节点」——后面没有真节点,这些空位就都在最右侧、合法。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 二叉树的完全性检验 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。