📌 题目链接:199. 二叉树的右视图 - 力扣(LeetCode)
🔍 难度:中等 | 🏷️ 标签:树、深度优先搜索(DFS)、广度优先搜索(BFS)、二叉树
⏱️ 目标时间复杂度:O(n)
💾 空间复杂度:O(h)(DFS)或 O(w)(BFS),其中 h 是树高,w 是最大层宽
🧠题目分析
给定一棵二叉树的根节点 root,要求返回从右侧视角看到的节点值,即每层最右边的节点,自上而下组成一个数组。
这道题的本质不是“一路向右走”,而是按层取最右节点。例如:
- 示例 1:
[1,2,3,null,5,null,4]→ 每层最右为[1,3,4] - 示例 2:
[1,null,3]→ 只有两层,结果为[1,3]
⚠️ 常见误区:直接 while(root) { ans.push(root->val); root = root->right; } —— 这是错误的!因为右子树可能为空,但左子树更深,此时右视图仍需包含左子树中的节点(如示例 2 的变种)。
🔑核心算法及代码讲解
本题有两种主流解法:广度优先搜索(BFS) 和 深度优先搜索(DFS) 。两者时间复杂度均为 O(n),但空间和实现思路不同。
✅ 方法一:广度优先搜索(BFS)—— 层序遍历取末尾
这是最直观的方法。利用队列进行层序遍历,每层只保留最后一个出队的节点值。
// BFS 解法:层序遍历,每层取最后一个节点
vector<int> rightSideView(TreeNode* root) {
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
vector<int> result;
while (!que.empty()) {
int size = que.size(); // 当前层的节点数
for (int i = 0; i < size; ++i) {
TreeNode* node = que.front();
que.pop();
if (i == size - 1) { // 👈 关键:当前是该层最后一个节点
result.push_back(node->val);
}
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
✅ 优点:逻辑清晰,易于理解,面试时可快速写出。
✅ 适用场景:需要按层处理的问题(如层平均值、每层最大值等)。
✅ 方法二:深度优先搜索(DFS)—— 先右后左,首次访问即答案
DFS 的核心思想是:按“根 → 右 → 左”顺序遍历,这样每一层第一个被访问到的节点,就是该层最右边的节点。
我们用一个 result 数组记录每层的答案。当递归到某一层 depth 时,若 result.size() == depth,说明这是该层第一次被访问,当前节点就是右视图所需节点。
// DFS 解法:先遍历右子树,再左子树
void dfs(TreeNode* node, int depth, vector<int>& result) {
if (!node) return;
if (depth == result.size()) { // 👈 关键:该深度首次访问
result.push_back(node->val);
}
dfs(node->right, depth + 1, result); // 先右!
dfs(node->left, depth + 1, result); // 再左
}
vector<int> rightSideView(TreeNode* root) {
vector<int> result;
dfs(root, 0, result);
return result;
}
✅ 优点:空间更优(递归栈深度 = 树高 h,通常 h << n)
✅ 面试加分点:展示对 DFS 遍历顺序的灵活控制能力
🧩解题思路(分步骤)
BFS 思路步骤:
-
初始化队列,将根节点入队(若非空)
-
当队列非空时,记录当前层节点数量
size -
循环
size次:- 出队一个节点
- 若是该层最后一个(
i == size - 1),加入结果 - 将其左右子节点(若存在)入队
-
返回结果数组
DFS 思路步骤:
- 从根节点开始,深度
depth = 0 - 若当前深度等于结果数组长度,说明首次到达该层,加入当前节点值
- 先递归右子树,再递归左子树(顺序不能反!)
- 递归终止条件:节点为空
📊算法分析
| 方法 | 时间复杂度 | 空间复杂度 | 适用性 |
|---|---|---|---|
| BFS | O(n) | O(w) | w 为最大层宽,最坏 O(n)(完全二叉树底层) |
| DFS | O(n) | O(h) | h 为树高,最坏 O(n)(退化为链表),平均 O(log n) |
💡 面试建议:
- 如果面试官问“能否用 DFS?”,一定要能写出先右后左的版本。
- 可对比两种方法:“BFS 更直观,DFS 更省空间”。
- 强调:右视图 ≠ 一直走 right 指针,这是常见陷阱!
💻代码
✅ C++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// Definition for a binary tree node.
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) {}
};
// ==================== BFS 解法 ====================
vector<int> rightSideView_BFS(TreeNode* root) {
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
vector<int> result;
while (!que.empty()) {
int size = que.size(); // 当前层的节点数
for (int i = 0; i < size; ++i) {
TreeNode* node = que.front();
que.pop();
if (i == size - 1) { // 👈 当前是该层最后一个节点
result.push_back(node->val);
}
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
// ==================== DFS 解法 ====================
void dfs(TreeNode* node, int depth, vector<int>& result) {
if (!node) return;
if (depth == result.size()) { // 👈 该深度首次访问
result.push_back(node->val);
}
dfs(node->right, depth + 1, result); // 先右!
dfs(node->left, depth + 1, result); // 再左
}
vector<int> rightSideView_DFS(TreeNode* root) {
vector<int> result;
dfs(root, 0, result);
return result;
}
// 测试
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
// 构建示例1: [1,2,3,null,5,null,4]
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->right = new TreeNode(5);
root->right->right = new TreeNode(4);
vector<int> ans1 = rightSideView_BFS(root);
vector<int> ans2 = rightSideView_DFS(root);
// 输出应为 [1, 3, 4]
for (int x : ans1) cout << x << " ";
cout << "\n";
for (int x : ans2) cout << x << " ";
cout << "\n";
return 0;
}
✅ JavaScript
// JavaScript 版本(BFS)
var rightSideView = function(root) {
if (!root) return [];
let res = [], queue = [root];
while (queue.length > 0) {
let size = queue.length;
for (let i = 0; i < size; i++) {
let node = queue.shift();
if (i === size - 1) { // 👈 该层最后一个
res.push(node.val);
}
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
}
return res;
};
// JavaScript 版本(DFS)
var rightSideView = function(root) {
const res = [];
function dfs(node, depth) {
if (!node) return;
if (depth === res.length) { // 👈 首次到达该层
res.push(node.val);
}
dfs(node.right, depth + 1); // 先右!
dfs(node.left, depth + 1); // 再左
}
dfs(root, 0);
return res;
};
🌟 本期完结,下期见!🔥
👉 点赞收藏加关注,新文更新不迷路。关注专栏【算法】LeetCode Hot100刷题日记,持续为你拆解每一道热题的底层逻辑与面试技巧!
💬 欢迎留言交流你的解法或疑问!一起进步,冲向 Offer!💪
📌 记住:当你在刷题时,不要只看答案,要像写这篇文章一样,深入思考每一步背后的原理、优化空间和面试价值。这才是真正提升算法能力的方式!