给定一棵二叉树的根节点 root,想象自己站在二叉树的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
示例:
输入:
root = [1,2,3,null,5,null,4]
对应二叉树:
1
/ \
2 3
\ \
5 4
从右侧观察:
1
3
4
输出:
[1,3,4]
另一个例子:
输入:
root = [1,null,3]
结构:
1
\
3
输出:
[1,3]
二、为什么这道题值得学习?
这道题是二叉树遍历中的经典问题,也是面试高频题。
它主要考察:
- 二叉树层序遍历
- BFS 广度优先搜索
- 如何获取每一层指定位置的节点
很多同学看到"右视图"可能会想到:
从右边一直遍历。
但实际上:
二叉树右视图的本质是:
每一层中最右边的节点。
例如:
1
/ \
2 3
/ \
4 5
按照层序遍历:
第一层:
1
第二层:
2 3
第三层:
4 5
右视图看到的就是:
1 3 5
所以:
只需要找到每一层最后访问的节点即可。
三、核心思想:层序遍历 + 记录每层最后一个节点
二叉树层序遍历:
也就是 BFS。
遍历顺序:
从上到下
从左到右
例如:
1
/ \
2 3
/ \
4 5
访问顺序:
1
2 3
4 5
如果我们知道:
当前层有多少个节点。
那么:
这一层最后被访问的节点:
就是右视图看到的节点。
所以算法步骤:
- 使用队列保存节点
- 每次处理一层节点
- 记录当前层最后一个节点
- 加入结果数组
四、解题思路分析
1. 使用队列进行层序遍历
首先定义队列:
Queue<TreeNode>
用于保存当前需要访问的节点。
初始化:
将根节点加入队列。
2. 获取当前层节点数量
每次进入循环:
记录当前层节点数量:
int size = queue.size();
例如:
当前队列:
[2,3]
说明:
这一层有两个节点。
3. 判断是否为当前层最后一个节点
遍历当前层:
for(int i = 0; i < size; i++)
当:
i == size - 1
说明:
当前节点是这一层最后访问的节点。
也就是:
右视图看到的节点。
加入答案:
result.add(node.val);
五、代码实现(BFS层序遍历)
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> result = new ArrayList<>();
if(root == null){
return result;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
// 当前层节点数量
int size = queue.size();
for(int i = 0; i < size; i++){
TreeNode node = queue.poll();
// 当前层最后一个节点
if(i == size - 1){
result.add(node.val);
}
// 左节点加入队列
if(node.left != null){
queue.offer(node.left);
}
// 右节点加入队列
if(node.right != null){
queue.offer(node.right);
}
}
}
return result;
}
}
六、过程图解
例如:
1
/ \
2 3
\ \
5 4
要求:
右视图
第一层
队列:
[1]
数量:
size = 1
访问:
1
因为:
i = size - 1
所以加入:
result = [1]
加入下一层:
[2,3]
第二层
队列:
[2,3]
数量:
size = 2
访问:
节点2:
i = 0
不是最后一个。
访问:
节点3:
i = 1
满足:
i == size - 1
加入:
result = [1,3]
第三层
队列:
[5,4]
访问:
节点5:
不是最后。
访问:
节点4:
最后一个节点。
加入:
result = [1,3,4]
最终答案:
[1,3,4]
七、复杂度分析
时间复杂度:
O(N)
原因:
每个节点都会被访问一次。
空间复杂度:
O(N)
原因:
队列最多存储一层节点。
在最坏情况下:
完全二叉树最后一层节点数量接近:
N/2
所以空间复杂度为:
O(N)
八、另一种方法:DFS递归
除了 BFS。
也可以使用深度优先遍历。
核心思想:
优先访问右子树。
因为:
右边节点更可能出现在右视图中。
遍历顺序:
根节点
↓
右子树
↓
左子树
例如:
1
/ \
2 3
\
4
访问:
1
3
4
2
每个深度第一次访问到的节点:
就是该层右侧节点。
代码:
class Solution {
List<Integer> result = new ArrayList<>();
public List<Integer> rightSideView(TreeNode root) {
dfs(root,0);
return result;
}
private void dfs(TreeNode root,int depth){
if(root == null){
return;
}
// 当前深度第一次访问
if(depth == result.size()){
result.add(root.val);
}
// 优先遍历右子树
dfs(root.right,depth + 1);
// 再遍历左子树
dfs(root.left,depth + 1);
}
}
九、两种方法比较
| 方法 | 思路 | 时间复杂度 | 空间复杂度 |
|---|---|---|---|
| BFS | 每层记录最后节点 | O(N) | O(N) |
| DFS | 优先访问右节点 | O(N) | O(H) |
面试中:
更加推荐:
✅ BFS层序遍历
因为它更加符合"右视图"的定义。
十、常见错误与避坑指南
❌ 错误一:只遍历右子树
很多人认为:
右视图就是一直走右孩子。
这是错误的。
例如:
1
/
2
\
3
如果只走右边:
只能得到:
1
但是实际右视图:
1
2
3
原因:
右视图看的是:
每层最右节点。
不是:
整棵树的右链。
❌ 错误二:记录每层第一个节点
错误:
i == 0
这样得到的是:
左视图。
右视图应该:
记录:
i == size - 1
❌ 错误三:忽略空树情况
如果:
root = null
应该返回:
[]
所以需要提前判断:
if(root == null)
十一、面试高频追问
1. 为什么 BFS 可以解决右视图?
因为:
BFS 按层遍历。
而右视图要求:
每层最右节点。
所以:
记录每层最后访问节点即可。
2. 为什么 DFS 要先访问右子树?
因为:
右子树节点优先被访问。
每个深度第一次出现的节点:
就是该层最右节点。
3. 如果要求左视图怎么办?
只需要改变遍历顺序:
左视图:
记录每层第一个节点。
DFS:
优先访问左子树。
总结
LeetCode 199 的核心思想是:
二叉树右视图 = 每一层最右侧节点。
通过:
层序遍历 BFS
控制每层节点数量:
遍历当前层
↓
记录最后一个节点
↓
加入答案
这道题不仅考察二叉树遍历,
还帮助理解:
- BFS在树结构中的应用
- 如何处理二叉树层级信息
- 每层节点的统计技巧