1. 问题理解与解法概述
今天我们来解决LeetCode上第199题"二叉树的右视图"。这个问题要求我们站在二叉树的右侧,返回从顶部到底部能看到的节点值。换句话说,我们需要找出每一层最右边的节点。
1.1 问题直观理解
想象你站在一棵大树的右侧,从树根到树顶看过去,哪些节点会进入你的视线?显然,每一层最靠近你的那个节点会挡住同层其他节点。这就是"右视图"的概念。
举个例子:
code复制 1
/ \
2 3
\ \
5 4
从右侧看,你会依次看到1、3、4这三个节点。
1.2 解法思路分析
解决这个问题主要有三种思路:
- BFS优化法:基于层序遍历,但调整节点入队顺序(先右后左),这样每层第一个节点就是最右侧节点。
- 标准BFS法:完全按照标准层序遍历,记录每层最后一个节点。
- DFS递归法:优先遍历右子树,首次访问某一层级时记录节点值。
这三种方法各有优缺点,下面我们会详细分析每种实现。
2. BFS优化法详解
2.1 算法核心思想
BFS优化法的核心在于调整节点入队顺序,让每一层的最右侧节点最先被处理。这样我们只需要记录每层第一个被访问的节点即可。
2.2 具体实现步骤
让我们仔细看看代码实现:
java复制public List<Integer> rightSideView(TreeNode root) {
if(root==null) {
return new ArrayList<>();
}
List<Integer> res=new ArrayList<>();
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
int size=queue.size();
for(int i=size;i>0;i--){
TreeNode node=queue.poll();
if(i==size) {
res.add(node.val);
}
if(node.right!=null){
queue.offer(node.right);
}
if(node.left!=null){
queue.offer(node.left);
}
}
}
return res;
}
2.3 关键点解析
- 入队顺序:先处理右子节点,再处理左子节点。这样下一层的最右侧节点会最先入队。
- 记录时机:当
i == size时,说明这是当前层第一个被处理的节点,也就是最右侧节点。 - 空树处理:如果根节点为null,直接返回空列表。
2.4 复杂度分析
- 时间复杂度:O(n),每个节点都会被访问一次
- 空间复杂度:O(n),最坏情况下队列需要存储一层所有节点
2.5 实际案例演示
以示例1为例:
code复制输入:root = [1,2,3,null,5,null,4]
执行过程:
- 第一层:[1],记录1
- 第二层:[3,2],记录3
- 第三层:[4,5],记录4
最终结果:[1,3,4]
3. 标准BFS层序遍历法
3.1 算法思路
这种方法采用标准的层序遍历方式,先左后右入队,然后记录每层最后一个节点。
3.2 代码实现
java复制public List<Integer> rightSideView(TreeNode root) {
if (root == null) {
return new ArrayList<>();
}
List<Integer> res = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
TreeNode lastNode = null;
for (int i = 0; i < size; i++) {
lastNode = queue.poll();
if (lastNode.left != null) {
queue.offer(lastNode.left);
}
if (lastNode.right != null) {
queue.offer(lastNode.right);
}
}
res.add(lastNode.val);
}
return res;
}
3.3 方法对比
与优化BFS法相比:
- 优点:逻辑更直观,符合常规层序遍历思维
- 缺点:必须遍历完整个层才能确定最右侧节点
3.4 适用场景
这种方法特别适合:
- 初学者理解层序遍历
- 需要同时获取左视图和右视图的情况
4. DFS递归解法
4.1 算法思想
DFS解法采用深度优先搜索,优先访问右子树。当首次到达一个新的深度时,记录当前节点值。
4.2 代码实现
java复制public List<Integer> rightSideView(TreeNode root) {
List<Integer> res = new ArrayList<>();
dfs(root, 0, res);
return res;
}
private void dfs(TreeNode node, int level, List<Integer> res) {
if (node == null) {
return;
}
if (level == res.size()) {
res.add(node.val);
}
dfs(node.right, level + 1, res);
dfs(node.left, level + 1, res);
}
4.3 关键点分析
- 层级判断:
level == res.size()表示首次访问该层级 - 遍历顺序:先右后左,确保首次访问的是最右侧节点
- 递归终止:节点为null时返回
4.4 复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(h),h为树高
4.5 优缺点
优点:
- 空间复杂度在平衡树情况下更优
- 代码简洁
缺点:
- 递归可能导致栈溢出(极端情况下)
- 逻辑相对抽象
5. 边界条件与异常处理
5.1 空树处理
所有解法都需要首先检查根节点是否为null:
java复制if(root == null) {
return new ArrayList<>();
}
5.2 单侧树情况
对于只有左子树或只有右子树的特殊情况,各解法都能正确处理:
code复制输入:[1,2,null,3,null,4]
输出:[1,2,3,4]
5.3 极端不平衡树
对于极端不平衡的树(如链表形式),DFS递归法可能会有栈溢出风险,而BFS法则更稳定。
6. 性能优化与实践建议
6.1 选择合适的方法
- 面试场景:推荐BFS优化法,展示对遍历顺序的理解
- 工程实践:标准BFS法更易维护
- 空间敏感:平衡树情况下DFS更优
6.2 常见错误与调试
- 忘记空树检查:导致NullPointerException
- 入队顺序错误:先左后右会导致结果错误
- 层级记录错误:DFS中level判断不正确
6.3 测试用例设计
建议测试以下情况:
- 空树
- 单节点树
- 完全左斜树
- 完全右斜树
- 普通二叉树
- 满二叉树
7. 扩展思考
7.1 左视图实现
只需调整遍历顺序:
- BFS法:先左后右入队,记录每层第一个节点
- DFS法:先左后右递归
7.2 俯视图实现
需要结合层级和水平位置信息,使用坐标映射。
7.3 其他变种
- 对角线视图
- 边界节点视图
- 之字形遍历
在实际开发中,理解这些树遍历的变种可以帮助我们更好地处理各种树形结构数据的展示需求。