【LeetCode热题100】Java详解:二叉树的右视图(含BFS/DFS双解法与工程实践)

【LeetCode热题100】Java详解:二叉树的右视图(含BFS/DFS双解法与工程实践)

面向人群

  • 正在准备技术面试(尤其是大厂后端、算法岗)的开发者
  • 已掌握基础二叉树操作,希望深入理解层序遍历与空间优化技巧的学习者
  • 刷 LeetCode「热题100」或「二叉树专题」的中级程序员
  • 对 BFS/DFS 应用场景与性能权衡感兴趣的工程人员

📌 本文适合已能手写二叉树前中后序遍历的读者。若对队列、栈等辅助数据结构不熟悉,建议先完成 LeetCode 第102题(二叉树的层序遍历)。

关键词

LeetCode 199二叉树的右视图BFSDFS层序遍历深度优先搜索广度优先搜索队列树的层级右视图面试高频题空间优化时间复杂度分析

阅读前必须掌握的基础

在深入阅读本文前,请确保你已熟练掌握以下知识点:

  1. 二叉树的基本结构

    • TreeNode定义(val,left,right
    • 树的深度(depth)与高度(height)概念
  2. 两种核心遍历思想

    • 深度优先搜索(DFS):使用递归或栈,沿一条路径走到尽头再回溯
    • 广度优先搜索(BFS):使用队列,按层从左到右访问节点
  3. Java 常用集合类

    • Queue<TreeNode>(通常用LinkedList实现)
    • Deque<TreeNode>(双端队列,可用于模拟栈)
    • HashMap<Integer, Integer>的基本操作
  4. 层序遍历(Level Order Traversal)

    • 能实现标准层序遍历(返回List<List<Integer>>
    • 理解“每层最后一个节点”的含义
  5. 时间与空间复杂度基础

    • 能分析 O(n)、O(log n)、O(h) 等常见复杂度
    • 理解最坏情况(如链状树)对空间的影响

✅ 推荐前置练习:

  • LeetCode 102:二叉树的层序遍历
  • LeetCode 104:二叉树的最大深度
  • LeetCode 107:二叉树的层序遍历 II

一、原题回顾

题目链接:199. 二叉树的右视图

题目描述

给定一个二叉树的根节点root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

示例 1:
输入:root = [1,2,3,null,5,null,4] 输出:[1,3,4]

可视化解释:

1 <—— 右视图看到 1 / \ 2 3 <—— 右视图看到 3(2被3挡住) \ \ 5 4 <—— 右视图看到 4(5被4挡住)
示例 2:
输入:root = [1,2,3,4,null,null,null,5] 输出:[1,3,4,5]
示例 3:
输入:root = [1,null,3] 输出:[1,3]
示例 4:
输入:root = [] 输出:[]
提示:
  • 二叉树的节点个数范围是[0, 100]
  • -100 <= Node.val <= 100

二、原题分析

2.1 什么是“右视图”?

“右视图”并非字面意义上的“所有右孩子”,而是指:从右侧垂直观察时,每一层中最后被看到的节点

关键洞察:

  • 同一层中,最右边的节点会挡住其左侧所有节点
  • 因此,右视图 =每层的最后一个节点(从左到右顺序)

✅ 举例:若某层节点为[A, B, C, D],则右视图看到的是D

2.2 为什么不能只取“右子树”?

反例:

1 / 2 \ 3
  • 右子树为空,但右视图应为[1,2,3]
  • 因为第2层只有2,第3层只有3,它们都是各自层的“最右”

因此,必须按层处理,而非简单遍历右分支。


三、答案构思:两种主流解法

本题的核心在于获取每层的最后一个节点。有两种经典思路:

方法核心思想数据结构访问顺序
方法一:DFS(深度优先)先访问右子树,每层首次访问即为最右节点栈(迭代)或递归右 → 左
方法二:BFS(广度优先)层序遍历,每层最后一个出队的节点即为答案队列左 → 右

💡 两种方法时间复杂度均为 O(n),但空间使用和实现细节有差异。


四、完整答案与代码实现(Java)

方法一:深度优先搜索(DFS)—— 先右后左

思路
  • 使用 DFS 遍历树,优先访问右子树
  • 维护一个Map<depth, value>,记录每个深度第一次访问到的节点值
  • 由于先访问右子树,第一次访问某深度时,该节点必为该层最右

✅ 优势:可提前终止(若只需前 K 层),但本题需全部层

Java 代码(迭代版)
classSolution{publicList<Integer>rightSideView(TreeNoderoot){if(root==null)returnnewArrayList<>();Map<Integer,Integer>rightmostValueAtDepth=newHashMap<>();intmaxDepth=-1;Deque<TreeNode>nodeStack=newLinkedList<>();Deque<Integer>depthStack=newLinkedList<>();nodeStack.push(root);depthStack.push(0);while(!nodeStack.isEmpty()){TreeNodenode=nodeStack.pop();intdepth=depthStack.pop();if(node!=null){// 更新最大深度maxDepth=Math.max(maxDepth,depth);// 只记录每个深度第一次出现的节点(即最右)if(!rightmostValueAtDepth.containsKey(depth)){rightmostValueAtDepth.put(depth,node.val);}// 注意:先压入左子树,再压入右子树!// 因为栈是 LIFO,后压入的先弹出nodeStack.push(node.left);nodeStack.push(node.right);depthStack.push(depth+1);depthStack.push(depth+1);}}// 按深度顺序构建结果List<Integer>result=newArrayList<>();for(intdepth=0;depth<=maxDepth;depth++){result.add(rightmostValueAtDepth.get(depth));}returnresult;}}
递归版(更简洁)
classSolution{publicList<Integer>rightSideView(TreeNoderoot){List<Integer>result=newArrayList<>();dfs(root,0,result);returnresult;}privatevoiddfs(TreeNodenode,intdepth,List<Integer>result){if(node==null)return;// 如果当前深度等于结果列表大小,说明这是该层第一个访问的节点if(depth==result.size()){result.add(node.val);}// 先递归右子树!dfs(node.right,depth+1,result);dfs(node.left,depth+1,result);}}

✨ 递归版巧妙之处:利用result.size()作为“已处理的最大深度”,无需额外 Map


方法二:广度优先搜索(BFS)—— 层序遍历

思路
  • 使用队列进行标准层序遍历
  • 对每一层,不断更新该层的“最右值”
  • 遍历完一层后,最后一次更新的值即为该层右视图节点

✅ 优势:逻辑直观,符合“按层处理”的直觉

Java 代码
classSolution{publicList<Integer>rightSideView(TreeNoderoot){if(root==null)returnnewArrayList<>();Map<Integer,Integer>rightmostValueAtDepth=newHashMap<>();intmaxDepth=-1;Queue<TreeNode>nodeQueue=newLinkedList<>();Queue<Integer>depthQueue=newLinkedList<>();nodeQueue.offer(root);depthQueue.offer(0);while(!nodeQueue.isEmpty()){TreeNodenode=nodeQueue.poll();intdepth=depthQueue.poll();if(node!=null){maxDepth=Math.max(maxDepth,depth);// 不断覆盖,最后一轮即为最右节点rightmostValueAtDepth.put(depth,node.val);// 先加左,再加右(保证层内从左到右)nodeQueue.offer(node.left);nodeQueue.offer(node.right);depthQueue.offer(depth+1);depthQueue.offer(depth+1);}}List<Integer>result=newArrayList<>();for(intdepth=0;depth<=maxDepth;depth++){result.add(rightmostValueAtDepth.get(depth));}returnresult;}}
优化版(无需 Map,直接取每层最后一个)
classSolution{publicList<Integer>rightSideView(TreeNoderoot){List<Integer>result=newArrayList<>();if(root==null)returnresult;Queue<TreeNode>queue=newLinkedList<>();queue.offer(root);while(!queue.isEmpty()){intsize=queue.size();// 遍历当前层所有节点for(inti=0;i<size;i++){TreeNodenode=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);}}returnresult;}}

✨ 此版本空间更优,无需存储深度信息,直接利用size控制层边界


五、代码分析与对比

维度DFS(递归)DFS(迭代)BFS(带 Map)BFS(优化版)
时间复杂度O(n)O(n)O(n)O(n)
空间复杂度O(h)O(h)O(n)O(w)
是否需要额外 Map
代码简洁性⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
可读性高(递归直观)高(层序清晰)
适用场景通用避免递归栈溢出教学演示推荐生产使用
  • h= 树高,w= 最大层宽
  • 平衡树:h = log n, w = n/2;链状树:h = n, w = 1
  • BFS 优化版是面试中最推荐的写法

六、时间复杂度与空间复杂度深度分析

时间复杂度:O(n)

  • 无论 DFS 还是 BFS,每个节点恰好被访问一次
  • 构建结果列表需 O(h) 或 O(levels),而 h ≤ n,故总时间 O(n)

空间复杂度

DFS(递归)
  • 递归栈深度= 树高h
  • 最好情况(平衡树):O(log n)
  • 最坏情况(链状树):O(n)
BFS(优化版)
  • 队列最大长度= 最宽层的节点数w
  • 最好情况(链状树):O(1)
  • 最坏情况(完全二叉树最后一层):O(n/2) = O(n)

💡 虽然最坏空间都是 O(n),但实际内存占用不同

  • DFS 在深而窄的树上更省空间
  • BFS 在宽而浅的树上更省空间

七、常见问题解答(FAQ)

Q1:为什么 DFS 要先访问右子树?

A:因为我们要确保每层第一次访问的节点是最右边的。若先访问左子树,则左节点会先被记录,导致错误。

Q2:BFS 中为什么可以不用 Map?

A:因为我们按层处理,每层循环结束时,最后一个节点就是最右节点,可直接加入结果,无需存储中间状态。

Q3:能否用前序/中序遍历解决?

A:不能。这些遍历无法自然按“层”组织节点,难以判断“是否为该层最后一个”。

Q4:如果要左视图怎么办?

A:两种方式:

  • DFS:改为先访问左子树
  • BFS:取每层第一个节点(i == 0

Q5:如何同时获取左右视图?

A:BFS 中同时记录i == 0i == size - 1的节点即可。


八、优化思路总结

8.1 空间优化

  • 避免使用 Map:BFS 优化版直接利用层循环
  • 避免存储深度:通过queue.size()控制层边界

8.2 代码简洁性优化

  • 递归 DFS:利用result.size()代替深度计数
  • 早期返回:空树直接返回空列表

8.3 扩展性优化

  • 若需支持“任意角度视图”(如第 K 个可见节点),可返回每层完整列表
  • 若树极大,可考虑迭代替代递归,防止栈溢出

九、数据结构与算法基础知识点回顾

9.1 树的遍历方式对比

遍历数据结构访问顺序适用场景
DFS(前序)栈/递归根→左→右复制树、序列化
DFS(中序)栈/递归左→根→右BST 升序
DFS(后序)栈/递归左→右→根释放内存
BFS(层序)队列按层从左到右求深度、宽度、视图

9.2 队列 vs 栈

特性队列(Queue)栈(Stack)
操作原则FIFO(先进先出)LIFO(后进先出)
Java 实现LinkedList,ArrayDequeStack,Deque
树遍历用途BFS(层序)DFS(深度优先)

9.3 复杂度分析要点

  • 时间:看节点访问次数
  • 空间
    • DFS:看递归深度或栈大小
    • BFS:看队列最大长度(即最大层宽)

十、面试官提问环节(模拟)

Q:BFS 优化版中,为什么queue.size()能代表当前层节点数?

A:因为在进入while循环时,队列中恰好包含当前层的所有节点。我们用size = queue.size()冻结该数量,然后通过for (int i=0; i<size; i++)精确处理这一层,同时将下一层节点加入队列,不影响当前层计数。

Q:如果要求返回“右视图的节点深度”,怎么办?

A:可在 BFS 中记录深度,或 DFS 中传递深度参数。例如递归 DFS:

if(depth==result.size()){result.add(node.val);depths.add(depth);// 同时记录深度}

Q:如何验证你的解法正确?

A:设计边界测试用例:

  • 空树 →[]
  • 单节点 →[val]
  • 完全左偏树 → 所有节点都可见
  • 完全右偏树 → 所有节点都可见
  • 深层左子树遮挡 → 如示例2

十一、实际开发中的应用场景

11.1 UI 渲染与布局

  • 在树形控件(如文件浏览器、组织架构图)中,右视图可表示最深层级的末级节点
  • 用于计算滚动区域高度或展开默认路径

11.2 网络拓扑可视化

  • 在网络设备层级图中,右视图可能代表边缘设备或终端节点
  • 辅助监控系统快速定位末端节点状态

11.3 编译器 AST(抽象语法树)

  • 右视图可能对应表达式中最右侧的操作数或变量
  • 用于静态分析或代码高亮

11.4 游戏开发(技能树、科技树)

  • 右视图可表示玩家当前解锁的最先进技能或科技
  • 用于成就系统或进度展示

十二、相关题目推荐

题号题目关联点
102二叉树的层序遍历BFS 基础
107二叉树的层序遍历 II自底向上层序
103二叉树的锯齿形层序遍历层序 + 方向交替
637二叉树的层平均值层序 + 聚合计算
116填充每个节点的下一个右侧节点指针层序 + 指针连接
513找树左下角的值左视图变种
199本题右视图

🔗 建议按此顺序刷题,形成“层序遍历”知识体系。


十三、总结与延伸

13.1 核心思想提炼

  • 右视图 = 每层最后一个节点
  • BFS 天然按层处理,是最直观解法
  • DFS 通过控制访问顺序(先右后左)也能实现
  • 空间优化的关键是避免冗余存储

13.2 延伸思考

  • 多叉树的右视图:同样适用 BFS,每层取最后一个子节点
  • N 叉树:LeetCode 429 题,解法完全一致
  • 带权右视图:若节点有权重,可能需要自定义“可见”规则
  • 动态右视图:若树动态变化,可结合增量更新策略

13.3 面试答题建议

不要一上来就写代码!

  1. 先澄清题意:“右视图是指每层最右边的节点,对吗?”
  2. 举小例子验证理解(如示例1)
  3. 提出两种思路(BFS/DFS),分析优劣
  4. 选择一种实现(推荐 BFS 优化版)
  5. 讨论边界情况和复杂度

掌握一道题,吃透一类题。
右视图的本质是“层序遍历的变种”,理解这一点,就能举一反三!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1179281.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker容器化实战:从入门到生产环境部署

前言 两年前&#xff0c;我们公司的部署流程是这样的&#xff1a;开发在本地调试好代码&#xff0c;打包发给运维&#xff0c;运维在服务器上配置环境&#xff0c;然后发现"在我机器上能跑"。 引入Docker后&#xff0c;一切都变了。这篇文章分享我们的容器化实践经…

栈的一个magic gadget的运用以及数组越界

the end???.text:0000000000400658 add [rbp-3Dh], ebx .text:000000000040065B nop .text:000000000040065C retn这个gadget就比较常见了,就是把ebx的值加给…

亲测好用!自考论文必备TOP9 AI论文工具深度测评

亲测好用&#xff01;自考论文必备TOP9 AI论文工具深度测评 一、不同维度核心推荐&#xff1a;9款AI工具各有所长 自考论文写作是一个系统性工程&#xff0c;从选题到开题、初稿撰写、查重降重再到最终排版&#xff0c;每一个环节都需要合适的工具辅助。而市面上的AI论文工具功…

【LeetCode热题100】Java详解:二叉树展开为链表(含O(1)空间原地解法与工程实践)

【LeetCode热题100】Java详解&#xff1a;二叉树展开为链表&#xff08;含O(1)空间原地解法与工程实践&#xff09; 面向人群 正在准备技术面试&#xff08;尤其是大厂后端、算法岗&#xff09;的开发者已掌握二叉树基本操作&#xff0c;希望深入理解原地算法与指针操作技巧的…

文献阅读:Class-incremental Learning for Time Series:Benchmark and Evaluation

摘要 现实世界的环境本质上是不稳定的&#xff0c;随着时间的推移经常引入新的类别。 这在时间序列分类中尤其常见&#xff0c;例如医疗保健中新疾病分类的出现或人类活动识别中添加新活动。 在这种情况下&#xff0c;需要一个学习系统来有效地吸收新的类&#xff0c;同时避免…

Day84(10)-F:\硕士阶段\Java\课程资料\7、Redis入门到实战教程\Redis-笔记资料\03-高级篇\资料\item-service-多级缓存

安装和配置Canal 下面我们就开启mysql的主从同步机制,让Canal来模拟salve 1.开启MySQL主从 Canal是基于MySQL的主从同步功能,因此必须先开启MySQL的主从功能才可以。 这里以之前用Docker运行的mysql为例: 1.1.开启b…

【LeetCode热题100】Java详解:二叉搜索树中第K小的元素(含进阶优化与面试延伸)

【LeetCode热题100】Java详解&#xff1a;二叉搜索树中第K小的元素&#xff08;含进阶优化与面试延伸&#xff09; 面向人群 正在准备技术面试&#xff08;尤其是大厂算法岗、后端开发岗&#xff09;的程序员已掌握基础数据结构&#xff0c;希望深入理解二叉搜索树及其应用场…

如何提高图像识别的准确率?

你想了解的是如何提升图像识别(以MNIST手写数字识别为例)的准确率,核心是从数据、模型、训练策略、正则化四个维度优化,解决“欠拟合”(准确率低)、“过拟合”(训练准、测试差)两大核心问题。下面我会结合MNIS…

数据结构入门:时间复杂度与排序和查找 - 详解

数据结构入门:时间复杂度与排序和查找 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &q…

STM32单片机16*16汉字点阵广告牌75(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

STM32单片机16*16汉字点阵广告牌75(设计源文件万字报告讲解)&#xff08;支持资料、图片参考_相关定制&#xff09;_文章底部可以扫码 产品功能描述&#xff1a; 本系统由STM32F103C8T6单片机核心板、16*16点阵屏显示模块、按键及电源组成。 1、通过按键可以切换点阵屏显示内容…

Meta 收购 Manus:AI 智能体由对话转向执行的转折点

在 2025 年的最后一天&#xff0c;Meta 公司通过官方渠道确认了对 AI 初创企业 Manus 的收购计划。根据相关分析机构披露的数据&#xff0c;这笔交易涉及金额预计超过 20 亿美元。这一变动不仅是 Meta 在人工智能领域扩张的延续&#xff0c;也反映出全球科技巨头正在将研发重点…

Python+django的旅游景点交通酒店预订网的设计与实现

目录设计背景与目标系统功能模块技术实现方案系统特色与创新应用价值与总结开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;设计背景与目标 随着旅游业的快速发展&#xff0c;游客对便捷的景…

【时频分析】基于matlab面向相交群延迟多分量信号的时频重分配同步挤压频域线性调频小波变换【含Matlab源码 14985期】复现含文献

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到海神之光博客之家&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49…

如何通过数据分析实现精准产品定位

如何通过数据分析实现精准产品定位 关键词:数据分析、精准产品定位、市场细分、用户画像、数据挖掘 摘要:本文旨在探讨如何利用数据分析来实现精准的产品定位。通过对市场数据、用户数据等多源数据的深入分析,我们可以更好地了解市场需求、用户偏好和竞争态势,从而为产品找…

day141—递归—二叉树的最大深度(LeetCode-104)

题目描述给定一个二叉树 root &#xff0c;返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。示例 1&#xff1a;输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a;输入&#xff1a;root [1,null,2] 输…

STM32-270-多功能水质监测系统(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

STM32-270-多功能水质监测系统(设计源文件万字报告讲解)&#xff08;支持资料、图片参考_相关定制&#xff09;_文章底部可以扫码 产品功能描述&#xff1a; 本系统由STM32F103C8T6单片机核心板、TFT1.44寸彩屏液晶显示电路、&#xff08;无线蓝牙/无线WIFI/无线视频监控模块-可…

基于图像模糊度统计和盲卷积滤波的图像去模糊算法matlab仿真

1.前言 基于图像模糊度统计和盲卷积滤波的图像去模糊算法,结合了对图像模糊程度的量化评估和无需预先知道模糊核的图像恢复技术,能够在一定程度上自动分析图像的模糊特性并进行有效复原。 2.算法运行效果图预览 (完整…

Python+django的同城社区篮球队管理系统 体育运动篮球赛事预约系统

目录同城社区篮球队管理系统摘要开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;同城社区篮球队管理系统摘要 该系统基于PythonDjango框架开发&#xff0c;旨在为社区篮球爱好者提供便捷的球…

Python+django的图书资料借阅信息管理系统的设计与实现

目录摘要开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;摘要 随着信息化时代的快速发展&#xff0c;图书资料的管理效率成为图书馆和各类机构关注的重点。传统的纸质记录方式效率低下且容易…

HTML打包EXE工具2.2.0版本重磅更新 - 2026年最新版本稳定性大幅提升

HTML打包EXE工具迎来2026年首个重要版本更新!2.2.0版本专注于稳定性提升和用户体验优化,修复了多个影响使用的关键问题,新增清理本地激活数据功能,为开发者提供更可靠的HTML转EXE解决方案。 软件官网 HTML打包EXE工…