【算法】Java-二叉树的右视图(BFS、DFS两种解法)

题目要求:

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

示例 1:

输入: [1,2,3,null,5,null,4]
输出: [1,3,4]

示例 2:

输入: [1,null,3]
输出: [1,3]

示例 3:

输入: []
输出: []

提示:

二叉树的节点个数的范围是[0,100]
-100<= Node.val <= 100


题目分析:

右视图,不仅仅是返回右子树上的节点,如果是一颗这样的树,我们要返回:1  3 5。

如果只返回右子树的节点,只返回1 3,就不符合右视图了。

返回每个层次最后一个节点,所以我用的第一个方法是:BFS-广度优先遍历。

方法一:广度优先遍历

思路:遍历每一层,将下一层的节点存到队列;如果是最后一个节点,add到返回集合;

图片来自LeetCode官方答案
public class RightViewTree {
	
	public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }

        ArrayDeque queue = new ArrayDeque(); 
        queue.offer(root);
        while (queue.size() > 0) {
            int count = queue.size(); // ①记录当前层有多少个节点
            for (int i = 0; i < count; i++) {
                TreeNode node = (TreeNode) queue.poll();
                if (node.getLeftNode() != null) {
                    queue.offer(node.getLeftNode());
                }
                if (node.getRightNode() != null) {
                    queue.offer(node.getRightNode());
                }
                // 遍历到最后一个 add 到res
                if (i == count - 1) {
                    res.add(node.val);
                }
            }
        }
        return res;
    }
}

另外:这是最终版,前面几版,除了把问题理解成了输出右子树的节点,还这样写过:

public List<Integer> rightSideView(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if(root == null){
            return res;
        }
        ArrayDeque queue = new ArrayDeque();
        queue.offer(root);
        int count = 1; // ①记录每层的节点个数
        while (queue.size()> 0){
            int add = 0;
            // 遍历队列 拿最后一个
            for(int i = 0; i < count ; i ++){
                TreeNode node =(TreeNode) queue.poll();
                if(node.getLeftNode() != null){
                    queue.offer(node.getLeftNode());
                    add ++;
                }
                if(node.getRightNode() != null){
                    queue.offer(node.getRightNode());
                    add ++;
                }
                // 遍历到最后一个 add 到res
                if(i == count -1){
                    res.add(node.val);
                }
            }
            count = add;
        }
        return res;
    }

时间复杂度:O(N);

空间复杂度:O(N);

①与最终版的区别在于,如何记录每层的节点数,这里用了一个临时变量count;

学习了其他博主的写法之后,意识到每次for循环结束后,add到queue中的节点,就是下一层所有节点了,不用单独记,直接取size可以。

(后来我想当时还是没把queue中存的内容想清楚,才又count了一下,所以在这里标识一下)

方法二:深度优先遍历

思路:按照根->右->左 遍历二叉树,保证每层都是最先访问最右边的节点。

图片来自LeetCode官方答案

      这个方法最开始我没想出来,后来看了别人的解题思路,理解到这种方法的关键在于:遍历每个节点时,如何知道这个深度是否已经记录过最右侧的节点了?大家看答案之前可以先想一想。

答:使用递归,将深度作为入参记录到栈帧。

将节点add到返回集合中;通过对比深度和集合中节点的数量,就知道该层是否已经记录过。

public class RightViewTree {


    private static List<Integer> res = new ArrayList<>();



    /**
     * 深度优先遍历
     * @param root
     * @return
     */
    private List<Integer> rightSideView(TreeNode root) {
        dfs(root, 0); // 从根节点开始访问,depth=0
        return res;
    }

  
    // 在使用递归进行深度优先遍历时,每个栈帧保存当时的depth。
    private void dfs(TreeNode root, int depth) {
        if (root == null) {
            return;
        }
        // 先访问 当前节点,再递归地访问右子树、左子树。
        // 每一层的depth一定,当depth>=res.size()时,说明该层还未记录,将节点add到返回集合中;在本题中,不会出现大于的情况,所以只需判断等于。
        // 又因为我们遍历的顺序是根-右-左,所以root一定是当前深度最右边的节点。
        if (depth == res.size()) {  
            res.add(root.val);
        }
        depth++; // 准备记录下一层
        dfs(root.getRightNode(), depth);
        dfs(root.getLeftNode(), depth);
    }


}

时间复杂度:O(N);

空间复杂度:O(N);

    对于深度优先遍历或递归不了解的朋友,可能对depth的变化不太理解。可以结合栈帧中记录,多debug几遍。

    方法二可以帮助我们更好的理解递归中,递和归的过程,以及过程中变量的变化。

写在最后:

      这道题的解法有很多,分享这两种解法的原因是在做这道题时,我感受到了自己的一点点质变。方法一,对队列的熟练应用。方法二,深度理解了递归的过程,后面还要继续应用,继续体会。

      学习算法增加了我看问题的视角,经常感叹“原来可以这样”,“居然还能这样”,“这人想的真好”之类。但我们知道“知道”和“做到”之间,有一条“鸿沟”,用新的“知道”的方法解出题的感觉真是太棒了,我做到了!当然,过程非常不容易,经常有些题目都看不懂,或者别人的思路理解不了。这时候,我们要想想,这道题难度是不是太大了,如果太大,要降低难度;否则,多debug,或者把步骤一步步写在纸上,是个不错的方法;或者多看看别人的解题思路。

     开年的第一篇博客,是关于对算法的思考,太不可思议了!小王师傅做到了!希望能帮到一些朋友,有更多的朋友做到!

参考:

LeetCode官方:. - 力扣(LeetCode)

LeetCode Sweetiee博主的分享:. - 力扣(LeetCode)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql原理--undo日志2

1.概述 上一章我们主要唠叨了为什么需要 undo日志 &#xff0c;以及 INSERT 、 DELETE 、 UPDATE 这些会对数据做改动的语句都会产生什么类型的 undo日志 &#xff0c;还有不同类型的 undo日志 的具体格式是什么。本章会继续唠叨这些 undo日志 会被具体写到什么地方&#xff0c…

LabVIEW利用视频分析实现高效硬度测量

LabVIEW利用视频分析实现高效硬度测量 在材料硬度测量领域&#xff0c;自动化和高精度测试技术的需求不断上升。布氏硬度机的自动化测量系统&#xff0c;尤其是那些结合了LabVIEW视频识别和处理技术的系统&#xff0c;正日益成为行业的焦点。介绍一个使用LabVIEW软件和先进的视…

mysql-实战案例 (超详细版)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…

用通俗易懂的方式讲解:大模型 RAG 技术,从入门到精通

本文基于IVAN ILIN发布于Towards AI的博客[1]进行总结归纳&#xff0c;感谢原作者的精彩讲解。 检索增强生成&#xff08;Retrieval Augmented Generation&#xff0c;简称RAG&#xff09;为大型语言模型&#xff08;LLMs&#xff09;提供了从某些数据源检索到的信息&#xff0…

svn spring项目增量打包工具

svn spring项目增量打包工具 前提介绍 项目使用svn &#xff0c;打包方式为war包&#xff0c;开发工具ide 项目有时候更新功能只需要更新部分class和html文件&#xff0c;但是要每个都打包并不是很简单 听说idea有现成的插件可以实现这个功能&#xff0c;但是我没找到&…

PPT插件-大珩助手-保留原素材的位置和大小一键替换

保留原素材的位置和大小一键替换 若勾选了一键替换&#xff0c;对于从素材库插入的图形&#xff0c;可以使得它的位置、大小与幻灯片中选中的形状一致 软件介绍 PPT大珩助手是一款全新设计的Office PPT插件&#xff0c;它是一款功能强大且实用的PPT辅助工具&#xff0c;支持W…

软件测试|QtDesigner配置以及使用

简介 上一篇文章我们介绍了PyQt5环境的安装和配置&#xff0c;并且安装了Qt tools工具&#xff0c;本文我们将介绍如何使用Qt tools的QtDesigner如何使用。 QtDesigner 的启动和入门 打开我们的项目从顶部菜单栏选择&#xff1a;Tools -> ExternalTools -> QtDesigner…

电脑重置网络后连不上网了怎么办

一般电脑重置网络后都会自动重新下载好网络配置&#xff0c;但是不免会出现一些意外&#xff0c;接下来就我遇到的重置后无法联网的解决方案 做一个分享&#xff1a; 1、按下“winR”打开运行输入 services.msc 。 2、找到 WLAN AutoConfig 和 Wired AutoConfig 服务&#xff…

class_3:lambda表达式

1、lambda表达式是c11引入的一种匿名函数的方式&#xff0c;它允许你在需要函数的地方内联的定义函数&#xff0c;而无需单独命名函数&#xff1b; #include <iostream>using namespace std;bool compare(int a,int b) {return a > b; }int getMax(int a,int b,bool (…

跟着cherno手搓游戏引擎【6】ImGui

导入ImGui&#xff1a; 下载链接&#xff1a; GitHub - TheCherno/imgui: Dear ImGui: Bloat-free Immediate Mode Graphical User interface for C with minimal dependencies 新建文件夹&#xff0c;把下载好的文件放入对应路径&#xff1a; SRC下的premake5.lua文件&#…

服务器感染了.pings勒索病毒,如何确保数据文件完整恢复?

导言&#xff1a; 随着科技的不断进步&#xff0c;网络犯罪也在不断演变。其中之一的.pings勒索病毒是一种危险的恶意软件&#xff0c;它能够加密用户的数据文件&#xff0c;并要求支付赎金以解密这些文件。在本文中&#xff0c;91数据恢复将介绍.pings勒索病毒&#xff0c;以…

Linux学习(1):目录结构、编辑器和用户管理

Linux学习&#xff08;1&#xff09;&#xff1a;目录结构、编辑器和用户管理 1 Linux目录结构2 vi和vim编辑器2.1 快捷键练习 3 用户管理3.1 添加用户3.2 删除用户即主目录3.3 切换用户 4 用户组 1 Linux目录结构 在linux世界里&#xff0c;一切皆为文件。 linux目录结构&a…

vue 自定义网页图标 favicon.ico 和 网页标题

效果预览 1. 添加配置 vue.config.js 在 module.exports { 内添加 // 自定义网页图标pwa: {iconPaths: {favicon32: "./favicon.ico",favicon16: "./favicon.ico",appleTouchIcon: "./favicon.ico",maskIcon: "./favicon.ico",msTil…

网站后台拿Webshell

通过注入或者其他途径&#xff0c;获取网站管理员账号和密码后&#xff0c;找到后台登录地址&#xff0c;登录后&#xff0c;寻找后台漏洞上传网页后门&#xff0c;获取网站的webshell webshell的作用是方便攻击者&#xff0c;webshel是拥有fso权限&#xff0c;根据fso权限的不…

[Docker] 基本名词

镜像(iamge)&#xff1a; Docker 镜像就好比是一个模板&#xff0c;可以通过这个模板来创建容器服务&#xff0c; 容器&#xff08;container&#xff09;: Docker利用容器技术&#xff0c;独立运行一个或则多个应用&#xff0c;通过镜像来创建的。 启动&#xff0c;停止&a…

RHCE9学习指南 第19章 网络时间服务器

19.1 时间同步的必要性 对于一些服务来说对时间要求非常严格&#xff0c;例如&#xff0c;图19-1所示由三台服务器搭建的ceph集群。 图19-1 三台机器搭建的集群对时间要求比较高 这三台服务器的时间必须要保持一样&#xff0c;如果不一样&#xff0c;就会显示报警信息。那么…

洛谷 P1442 铁球落地【线性dp+线段树预处理+离散化】

原题链接&#xff1a;https://www.luogu.com.cn/problem/P1442 题目描述 在二维坐标系内有 n 个平台&#xff08;定义平台是一条两端点纵坐标相同的开线段&#xff0c;开线段指线段两个端点不算做线段本身&#xff09;和一个铁球&#xff0c;铁球如果下面没有物体&#xff0c…

轻量级接口自动化测试框架

大致思路: jmeter完成接口脚本,Ant完成脚本执行并收集结果生成报告,最后利用jenkins完成脚本的自动集成运行. 环境安装: 1.jdk1.7 配置环境变量(参考前面的分页) 2.jmeter解压到本地,ant解压到本地 3.Ant解压到本地,并配置环境变量 ANT_HOME:D:\jmeter\apache-ant-1.9.6 P…

玩转Mysql 八 (MySQ优化入门篇)

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。如有侵权&#xff0c;请留言&#xff0c;我及时删除&#xff01; 前言&#xff1a; 一个高性能&#xff0c;稳定的数据库集群并不是指的某一特性优化&#xff0c;就…

嵌入式软件开发人员有必要学习系统移植的知识吗?【ppt获取见文末】

《从零开始学ARM》的配套视频说明 为了让粉丝更好的学习我的新书里面的知识&#xff0c; 一口君特地录制了配套学习视频&#xff0c; 《从0学ARM第一期》 《从0学ARM第一期》 视频已经免费发布在B站&#xff0c; 而书中除了ARM汇编、裸机开发等知识&#xff0c;还涉及到…