算法通关村第六关—二叉树的层次遍历经典问题(白银)

         二叉树的层次遍历经典问题

一、层次遍历简介

广度优先遍历又称层次遍历,过程如下:截屏2023-12-01 13.05.51.png
 层次遍历就是从根节点开始,先访问根节点下面一层全部元素,再访问之后的层次,图里就是从左到右一层一层的去遍历二叉树,先访问3,之后访问的左右子孩子9和20,之后分别访问9和20的左右子孩子[8,13]和[15,17],最后得到结果[3,9,20,8,13,15,17]。
 这里的问题是怎么将遍历过的元素的子孩子保存一下呢,例如访问9时其左右子孩子8和13应该先存一下,直到处理20之后才会处理。使用队列来存储能完美解决上述问题,例如上面的图中:
截屏2023-12-01 13.08.13.png
 思考:该过程不复杂,如果能将树的每层次分开了,是否可以整点新花样?首先,能否将每层的元素顺序给反转一下呢?能否奇数行不变,只将偶数行反转呢?能否将输出层次从低到root逐层输出呢?再来,既然能拿到每一层的元素了,能否找到当前层最大的元素?最小的元素?最右的元素(右视图)?最左的元素(左视图)?整个层的平均值?
 很明显都可以!这么折腾有啥用呢?没啥用!但这就是层次遍历的高频算法题!这就是LeetCode里经典的层次遍历题!
102.二叉树的层序遍历
107.二叉树的层次遍历II
199.二叉树的右视图
637.二叉树的层平均值
429.N叉树的前序遍历
515.在每个树行中找最大值
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针l
103锯齿层序遍历
除此之外,在深度优先的题目里,有些仍然会考虑层次遍历的实现方法。

二、基本的层序遍历与变换

2.1 层序遍历基础版

最简单的情况:仅仅层序遍历并输出所有元素
利用前面讲到的借助队列的方法来实现

List<Integer>simpleLevelorder(TreeNode root){
	if(root == null){
		return new ArrayList<Integer>();
	}
	List<Integer>res = new ArrayList<Integer>();
	LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
	//将根节点放入队列中,然后不断遍历队列
	queue.add(root);
	//有多少元素执行多少次
	while (queue.size()>0){
		//获取当前队列的长度,这个长度相当于当前这一层的节点个数
		TreeNode t = queue.remove();
		res.add(t.val);
		if(t.left != null){
			queue.add(t.left);
		}
		if(t.right = null){
			queue.add(t.right);
		}
	}
	return res;
}

2.2 二叉树的层序遍历(模版:非常重要)

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c23f7ece838341c491b3918718f405e5.pn
LeetCode102:给你一个二叉树,请你返回其按层序遍历得到的节点值。(即逐层地,从左到右访问所有节点)。
注意:与前面相比,要区分每一层
截屏2023-12-01 13.24.31.png
如何判断某一层遍历完了?可以设定一个size变量来标记。size表示某一层的元素个数,只要元素出队,size-1.当size==0时,该层已经遍历完了。(代码里的做法与这里殊途同归)
然后此时队里的元素个数为下一层元素的个数,因此让size标记为下一层的元素个数即可处理下一层了。
最后,把每一层遍历到的结点放入一个结果集中,将其返回即可

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        if(root == null) return new ArrayList<List<Integer>>();
		//创建集合和队列
        List<List<Integer>> list = new ArrayList<>();
        LinkedList<TreeNode> queue = new LinkedList();
		//将根结点放入队列,方便进入循环
        queue.add(root);
        while(queue.size() > 0){
			//everylist集合用来记录某一层的所有元素
             List<Integer> everylist = new ArrayList<>();
			//size记录某一层的元素个数
            int size = queue.size();
			//从队头遍历某一层的所有元素,并把下一层的元素存放到队尾
            for(int i = 0; i < size; i++){
                 TreeNode t = queue.remove();
                 everylist.add(t.val);
                if(t.left != null)queue.add(t.left);
                if(t.right != null)queue.add(t.right);
            }
			//此时i == size,该层所有元素都遍历完了
            list.add(everylist);
        }
        return list;
    }
}

2.3 层序遍历-自底往上

LeetCode107.给定一个二叉树,返回其节点值自底向上的层序遍历。(即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)。例如给定的二叉树与结果为下图:
截屏2023-12-01 15.06.52.png 截屏2023-12-01 15.07.00.png
实现的代码几乎与正常的层序遍历相同,只在list.add(0,everylist)这里把后面遍历的层次放到list的前面,实现自底往上的效果。

为了降低在结果列表的头部添加一层节点值的列表的时间复杂度,结果列表可以使用链表的结构,即创建list时把ArrayList改成LinkedList。在链表头部添加一层节点值的列表的时间复杂度是O(1)。

class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        if(root == null) return new LinkedList<List<Integer>>();
        List<List<Integer>> list = new LinkedList<>();
        LinkedList<TreeNode> queue = new LinkedList();
        queue.add(root);
        while(queue.size() > 0){
             List<Integer> everylist = new ArrayList<>();
            int size = queue.size();
            for(int i = 0; i < size; i++){
                 TreeNode t = queue.remove();
                 everylist.add(t.val);
                if(t.left != null)queue.add(t.left);
                if(t.right != null)queue.add(t.right);
            }
            list.add(0,everylist);
        }
        return list;
    }
}

2.4 二叉树的锯齿形层次遍历

LeetCode103题,要求是:给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
例如:给定二叉树[3,9,20,nul,null,15,7]
截屏2023-12-01 15.16.43.png 截屏2023-12-01 15.16.50.png
依旧是对2.2对代码进行改写。因为这里有时是从左到右,有时是从右到左,自然可以想到把代码里的队列改成双端队列,然后再设置过参数判断元素存放位置。
如果从左至右,我们每次将被遍历到的当前层元素插入至双端队列的末尾。如果从右至左,我们每次将被遍历到的元素插入至双端队列的头部。

class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        if(root == null) return new ArrayList<List<Integer>>();
        List<List<Integer>> list = new LinkedList<>();
        Queue<TreeNode> queue = new LinkedList();
        queue.add(root);
        Boolean judgeleft = true;
        while(queue.size() > 0){
            //创建成双端队列
             Deque<Integer> everylist = new LinkedList<>();
            int size = queue.size();
			
            for(int i = 0; i < size; i++){
                TreeNode t = queue.remove();
                //从左到右放队尾
                if(judgeleft) everylist.offerLast(t.val);
                //从右到左放队首
                else everylist.offerFirst(t.val);
                if(t.left != null)queue.add(t.left);
                if(t.right != null)queue.add(t.right);
            }
            judgeleft = !judgeleft;
            //把双端队列转类型
            list.add(new LinkedList<Integer>(everylist));
        }
        return list;
    }
}

2.5 N叉树的层序遍历

LeetCode.429给定一个N叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)树的序列化输入是用层序遍历,每组子节点都由null值分隔(参见示例)
截屏2023-12-01 17.55.43.png
N叉树的定义如下,就是一个值,加一个列表,其类型仍然是Node:截屏2023-12-01 17.55.52.png
这个也是102的扩展,很简单的广度优先,与二叉树的层序遍历基本一样,借助队列即可实现。只有在保存当前结点的孩子时,不是左右孩子,有N个,可以使用增强for循环来遍历

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        if(root == null)return new ArrayList<List<Integer>>();
        List<List<Integer>> list = new ArrayList();
        Queue<Node> queue = new LinkedList();
        queue.add(root);
        while(queue.size() > 0){
            List<Integer> everylist = new ArrayList();
            int size = queue.size();
            for(int i = 0; i < size; i++){
                Node t = queue.remove();
                everylist.add(t.val);
				//与2.2唯一不同之处,利用增强for循环遍历所有孩子
                for(Node node1: t.children) {
                    if(node1 != null) queue.add(node1);
                }
            }
            list.add(everylist);
        }
        return list;
    }
}

三、几个处理每层元素的题目

如果我们拿到了每一层的元素,那是不是可以利用一下造几个题呢?例如每层找最大值、平均值、最右侧的值呢?当然可以。LeetCode.里就有三道非常明显的题目。
515.在每个树行中找最大值
637.二叉树的层平均值
199.二叉树的右视图

3.1 在每个树行中找最大值

LeetCode515题目要求:给定一棵二叉树的根节点root,请找出该二叉树中每一层的最大值。
截屏2023-12-01 18.04.21.png
与模版相比,在while循环中不需要创建另外一个集合,只需要用max记录最大值,然后添加进list集合即可

class Solution {
    public List<Integer> largestValues(TreeNode root) {
        if(root == null) return new ArrayList<Integer>();
        List<Integer> maxlist = new ArrayList();
        Queue<TreeNode> queue = new LinkedList();
        queue.add(root);
        while(queue.size() > 0){
            int max = Integer.MIN_VALUE;
            int size = queue.size();
            for(int i = 0; i < size; i++){
                TreeNode t =queue.remove();
                max = Math.max(max,t.val);
                if(t.left != null) queue.add(t.left);
                if(t.right != null) queue.add(t.right);
            }
            maxlist.add(max);
        }
        return maxlist;
    }
}

3.2 在每个树行中找平均值

LeetCode637要求给定一个非空二叉树,返回一个由每层节点平均值组成的数组
这些题目其实都差不多一个思路,只要中间一些处理修改一下就行。这道题就把每层元素的和记录下来,除以元素个数就行

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> list = new ArrayList();
        if(root == null) return list;
        Queue<TreeNode> queue = new LinkedList();
        queue.add(root);
        while(queue.size() > 0){
            int size = queue.size();
            double sum = 0;
            for(int i = 0; i < size; i++){
                TreeNode t = queue.remove();
                sum += t.val;
                if(t.left != null) queue.add(t.left);
                if(t.right != null) queue.add(t.right);
            }
            list.add(sum/size);
        }
        return list;
    }
}

3.3 二叉树的右视图

LeetCode199题目要求是:给定一个二叉树的根节点root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。例如:
截屏2023-12-01 22.22.05.png
这道题只要把每层最后一个元素保存起来即可

class Solution {
    public List<Integer> rightSideView(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null) return list;
        Queue<TreeNode> queue = new LinkedList();
        queue.add(root);
        while(queue.size() > 0){
            int size = queue.size();
            for(int i = 0; i < size; i++){
                TreeNode t = queue.remove();
                if(size - 1 == i) list.add(t.val);
                if(t.left != null) queue.add(t.left);
                if(t.right != null) queue.add(t.right);
            }
        }
        return list;
    }
}

3.4 最底层最左边

上面这个层次遍历的思想可以方便的解决LeetCode513.二叉树最底层最左边的值的问题:给定一个二叉树的根节点root,请找出该二叉树的最底层最左边节点的值。假设二叉树至少有一个节点。
截屏2023-12-01 22.26.25.png截屏2023-12-01 22.26.32.png
如果是正常遍历,是从左到右,那最后会遍历到最底层最右边,与题目正好相反。所以,我们在遍历存储下一层元素的时候,可以先保存右边的节点,再保存左边的节点。这样,遍历就是从右到左,题目就解出来啦。

class Solution {
    public int findBottomLeftValue(TreeNode root) {
		//可以记录值;也可记录节点,最后返回值
        int num = 0;
        Queue<TreeNode> queue = new LinkedList();
        queue.add(root);
        while(queue.size() > 0){
            int size = queue.size();
            for(int i = 0; i < size; i++){
                TreeNode t = queue.remove();
                num = t.val;
                if(t.right != null) queue.add(t.right);
                if(t.left != null) queue.add(t.left);
            }
        }
        return num;
    }
}

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

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

相关文章

LangChain的函数,工具和代理(三):LangChain中轻松实现OpenAI函数调用

在我之前写的两篇博客中:OpenAI的函数调用,LangChain的表达式语言(LCEL)中介绍了如何利用openai的api来实现函数调用功能&#xff0c;以及在langchain中如何实现openai的函数调用功能&#xff0c;在这两篇博客中&#xff0c;我们都需要手动去创建一个结构比较复杂的函数描述变量…

leetCode 131.分割回文串 + 动态规划 + 回溯算法 + 优化 + 图解 + 笔记

我的往期文章&#xff1a; leetCode 647.回文子串 动态规划 优化空间 / 中心扩展法 双指针-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/133883091?spm1001.2014.3001.5501leetCode 131.分割回文串 回溯算法 图解 笔记-CSDN博客https://blog.csdn.n…

杏色主题卧室书房一体装修,温馨舒适的不二之选!福州中宅装饰,福州装修

分享一间暖杏色系卧室装修案例&#xff0c;希望可以给你们一些启发&#xff01; 01.配色&#xff1a;杏色&#xff1b;浅杏色&#xff1b;浅咖色&#xff1b;咖色&#xff1b;茶色 你是否想要一个宁静而优雅的居室&#xff0c;融合了卧室与书房的功能&#xff0c;提供既实用又…

java基础语法总结

导言&#xff1a; Java语言是一种面向对象的编程语言&#xff0c;具有简单、可移植、安全、高性能等特点。本篇文章主要对java的基础的语法进行一个简单的总结和概述。 目录 导言&#xff1a; 正文&#xff1a; 1. 数据类型与变量 2. 运算符与逻辑控制 3. 方法 4. 数组…

如何在C/C++中测量一个函数或者功能的运行时间(串行和并行,以及三种方法的实际情况对比)

本文算是一个比较完整的关于在 C/C 中测量一个函数或者功能的总结&#xff0c;最后会演示三种方法的对比。 最常用的clock() 最常用的测量方法是使用clock()来记录两个 CPU 时间点clock_t&#xff0c;然后做差。这个方法的好处在于非常简单易写&#xff0c;如下&#xff08;第…

探究Kafka原理-4.API使用

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44…

程序员成了teamleader

在职场中,你是否遇到过这样的领导或同事,他可能是自恋狂,自吹自擂自我标榜;可能是团队合作的绊脚石,对团队合作态度消极并频繁拖后腿;可能是抱怨专家,满满负能量;可能是完美主义者,对细节过度挑剔;可能是技术白痴,对技术一窍不通或总是犯低级错误;可能是抢功劳者,…

消息中间件介绍

概述 消息队列已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能&#xff0c;成为异步RPC的主要手段之一。当今市面上有很多主流的消息中间件&#xff0c;如ActiveMQ、RabbitMQ&#xff0c;Kafka&#xff0c;还有阿里…

【工具分享】| 阅读论文神器 使用技巧 AI润色 AI翻译

文章目录 1 使用技巧1.1 功能一 即时翻译1.2 功能二 文献跳转1.3 功能三 多设备阅读1.4 功能四 小组讨论笔记共享1.5 功能五 个人文献管理 2 其他功能 超级喜欢Readpaper这一款论文阅读软件&#xff0c;吹爆他哈哈 为什么&#xff1f; 当然是他可以解决我们传统阅读论文的种种…

Python列表切片操作详解:提取、复制、反转等应用示例

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在Python中&#xff0c;列表切片是处理列表数据非常强大且灵活的方法。本文将全面探讨Python中列表切片的多种用法&#xff0c;包括提取子列表、复制列表、反转列表等操作&#xff0c;结合丰富的示例代码进行详细…

模糊C均值(Fuzzy C-means,FCM)聚类的可运行的python程序代码,复制即可用!!切记需要安装库 scikit-fuzzy

文章目录 前言一、安装库 scikit-fuzzy二、具体程序代码&#xff08;复制可运行&#xff09;三、结果展示总结 前言 模糊C均值&#xff08;Fuzzy C-means&#xff0c;FCM&#xff09;聚类是一种软聚类方法&#xff0c;它允许数据点属于多个聚类&#xff0c;每个数据点对所有聚…

c语言练习13周(1~5)

输入任意整数n求以下公式和的平方根。 读取一系列的整数 X&#xff0c;对于每个 X&#xff0c;输出一个 1,2,…,X 的序列。 编写double fun(int a[M][M])函数&#xff0c;返回二维数组周边元素的平均值&#xff0c;M为定义好的符号常量。 编写double fun(int a[M])函…

DAPP开发【02】Remix使用

系列文章目录 系列文章在DAPP开发专栏 文章目录 系列文章目录使用部署测试网上本地项目连接remix本地项目连接remix 使用 创建一个新的工作空间 部署测试网上 利用metaMask连接测试网络 添加成功&#xff0c;添加时需要签名 即可进行编译 即可部署 本地项目连接remix 方…

赛氪受邀参加“CCF走进高校”,助力计算机学科发展

赛氪受邀参加“CCF走进高校”&#xff0c;助力计算机学科发展。 12月1日&#xff0c;由中国计算机学会计算机应用专业委员会组织的第十二届第十六次常务委员(扩大)会议顺利召开&#xff0c;赛氪受邀参加。 本次会议“CCF走进东北师大以及长春工业大学”&#xff0c;围绕《水声…

【tailwind CSS ml 不生效】

tailwind官方文档中需要注意的一点是&#xff0c;margin或者padding的值最大就到96&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 附上官方文档链接tailwind官方文档

云计算如何创芯:“逆向工作法”的性感之处

在整个云计算领域&#xff0c;能让芯片规模化的用起来&#xff0c;是决定造芯是否成功的天花板。在拉斯维加斯的亚马逊云科技2023 re:Invent则是完美诠释了这一论调。 亚马逊云科技2023 re:Invent开幕前两个小时&#xff0c;有一场小型的欢迎晚宴&#xff0c;《星期日泰晤士报》…

带你手搓阻塞队列——自定义实现

&#x1f308;&#x1f308;&#x1f308;今天给大家分享的是——阻塞队列的自定义实现&#xff0c;通过自定义实现一个阻塞队列&#xff0c;可以帮助我们更清晰、更透彻的理解阻塞队列的底层原理。 清风的CSDN博客 &#x1f6e9;️&#x1f6e9;️&#x1f6e9;️希望我的文章…

喜报 | 通付盾WAAP解决方案入选国家工业信息安全发展研究中心“2023年数字化转型自主创新解决方案优选案例”

为提升自主创新产品质量和技术创新能力&#xff0c;助力重点行业自主可控基础设施建设&#xff0c;加速重点行业数字化转型工作进程&#xff0c;促进重点行业产业链数字化升级&#xff0c;推动重点行业数字化、网络化、智能化发展。国家工业信息安全发展研究中心联合中国交通建…

SQL手工注入漏洞测试(MySQL数据库-字符型)-墨者

———靶场专栏——— 声明&#xff1a;文章由作者weoptions学习或练习过程中的步骤及思路&#xff0c;非正式答案&#xff0c;仅供学习和参考。 靶场背景&#xff1a; 来源&#xff1a; 墨者学院 简介&#xff1a; 安全工程师"墨者"最近在练习SQL手工注入漏洞&#…

SpringBoot——Quartz 定时任务

优质博文&#xff1a;IT-BLOG-CN 一、Scheduled 定时任务 【1】添加Scheduled相关依赖&#xff0c;它是Spring自带的一个jar包因此引入Spring的依赖&#xff1a; <dependency><groupId>org.springframework</groupId><artifactId>spring-context-su…
最新文章