二叉树的层次遍历经典问题-算法通关村

二叉树的层次遍历经典问题-算法通关村


1 层次遍历简介

  • 广度优先在面试里出现的频率非常高,整体属于简单题。广度优先又叫层次遍历,基本过程如下:

  • 层次遍历就是从根节点开始,先访问根节点下面一层全部元素,再访问之后的层次,类似金字塔一样一层层访问。我们可以看到这里就是从左到右一层一层的去遍历二叉树,先访问3,之后访问3的左右子孩子9和20,之后分别访问9和20的左右子孩子[8,13]和[15,17],最后得到结果
    [3,9,20,8,13,15,17]。
    这里的问题是怎么将遍历过的元素的子孩子保存一下呢,例如访问9时其左右子孩子8和13应该先存一下,直到处理20之后才会处理。使用队列来存储能完美解决上述问题,例如上面的图中:

  • 1. 首先3入队 2.然后3出队,之后将3的左右孩子9和20,保存到队列中。 3.之后9出队,将9的左右孩子8和13入队。 4.之后20出队,将20的左右孩子15和7入队。 5.之后 8,13,15,7分别出队,此时都是叶子结点,只出队就行。
  • 这就是LeetCode里经典的层次遍历题!102.二叉树的层序遍历,107.二叉树的层次遍历II,199.二叉树的右视图,637.二叉树的层平均值,429.N叉树的前序遍历,515.在每个树行中找最大值,116.填充每个节点的下一个右侧节点指针,117.填充每个节点的下一个右侧节点指针II,103 锯齿层序遍历
    除此之外,在深度优先的题目里,有些仍然会考虑层次遍历的实现方法。


2 基本的层序遍历与变换

  • 仅仅遍历并输出全部元素

  •   public List<Integer> simpleLevelOrder(TreeNode root){if(root == null){return new ArrayList<Integer>();}List<Integer> list = new ArrayList<>();Deque<TreeNode> TreeQueue = new ArrayDeque<>();//将根节点放入队列中,然后不断遍历TreeQueue.offer(root);while(!TreeQueue.isEmpty()){//一层一层的遍历for(int i = 0; i< TreeQueue.size(); i++){TreeNode temp = TreeQueue.poll();list.add(temp.val);if(temp.left != null){TreeQueue.offer(temp.left);}if(temp.right != null){TreeQueue.offer(temp.right);}}}return list;}
    

2.1二叉树的层序遍历

  • LeetCode102:给你一个二叉树,请你返回其按层次遍历得到的节点值。(即逐层地,从左到右访问所有节点)。

  • 如何判断某一层访问完了呢?简单,用一个变量size标记一下就行了,size表示某一层的元素个数,只要出队,就将size减1,减到O就说明该层元素访问完了。当size变成0之后,这时队列中剩余元素的个数恰好就是下一层元素的个数,因此重新将size标记为下一层的元素个数就可以继续处理新的一行了。最后,把每层遍历到的节点都放到一个结果集中,将其返回就行了。

  •   public List<List<Integer>> levelOrder(TreeNode root){if(root == null){return new ArrayList<List<Integer>>();}List<List<Integer>> res = new ArrayList<>();Deque<TreeNode> TreeQueue = new ArrayDeque<>();//将根节点放入队列中,然后不断遍历TreeQueue.offer(root);while(!TreeQueue.isEmpty()){//存储每一层的元素List<Integer> list = new ArrayList<>();for(int i = 0; i< TreeQueue.size(); i++){TreeNode temp = TreeQueue.poll();list.add(temp.val);if(temp.left != null){TreeQueue.offer(temp.left);}if(temp.right != null){TreeQueue.offer(temp.right);}}res.add(list);}return res;}
    

2.2 层序遍历-自底向上

  • LeetCode 107.给定一个二叉树,返回其节点值自底向上的层序遍历。(即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)。例如给定的二叉树为:

  • [ [15, 7], [9, 20], [3] ]
  • 如果要求从上到下输出每一层的节点值,做法是很直观的,在遍历完一层节点之后,将存储该层节点值的列表添加到结果列表的尾部。这道题要求从下到上输出每一层的节点值,只要对上述操作稍作修改即可,在遍历完一层节点之后,将存储该层节点值的列表添加到结果列表的头部

  •   public List<List<Integer>> levelOrderBottom(TreeNode root){if(root == null){return new ArrayList<List<Integer>>();}List<List<Integer>> res = new ArrayList<>();Deque<TreeNode> TreeQueue = new ArrayDeque<>();//将根节点放入队列中,然后不断遍历TreeQueue.offer(root);while(!TreeQueue.isEmpty()){//存储每一层的元素List<Integer> levelList = new ArrayList<>();for(int i = 0; i< TreeQueue.size(); i++){TreeNode temp = TreeQueue.poll();levelList.add(temp.val);if(temp.left != null){TreeQueue.offer(temp.left);}if(temp.right != null){TreeQueue.offer(temp.right);}}//当前层的节点值列表 levelList 添加到结果列表 res 的开头,// 实现了层次遍历结果的逆序存储。res.add(0, levelList);}return res;}
    

2.3二叉树的锯齿形层序遍历

  • LeetCode103 :给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
    例如:给定二叉树 [3,9,20,null,null,15,7]

  • [ [3], [20, 9] [15, 7] ]
  • 这个题也是102的变种,只是最后输出的要求有所变化,要求我们按层数的奇偶来决定每一层的输出顺序。如果当前层数是偶数,从左至右输出当前层的节点值,否则,从右至左输出当前层的节点值。

  • 我们依然可以沿用第102题的思想,为了满足题目要求的返回值为**「先从左往右,再从右往左」交替输出的锯齿形,可以利用「双端队列」**的数据结构来维护当前层节点值输出的顺序。双端队列是一个可以在队列任意一端插入元素的队列。在广度优先搜索遍历当前层节点拓展下一层节点的时候我们仍然从左往右按顺序拓展,但是对当前层节点的存储我们维护一个变量isOrderLeft
    记录是从左至右还是从右至左的:
    • 如果从左至右,我们每次将被遍历到的元素插入至双端队列的末尾。
    •从右至左,我们每次将被遍历到的元素插入至双端队列的头部。

  •   public List<List<Integer>> zigzagLevelOrder(TreeNode root){if(root == null){return new ArrayList<List<Integer>>();}List<List<Integer>> res = new ArrayList<>();Deque<TreeNode> TreeQueue = new ArrayDeque<>();//将根节点放入队列中,然后不断遍历TreeQueue.offer(root);boolean isOrderLeft = true;while(!TreeQueue.isEmpty()){//存储每一层的元素Deque<Integer> levelQueue = new ArrayDeque<>();for(int i = 0; i< TreeQueue.size(); i++){TreeNode temp = TreeQueue.poll();if(isOrderLeft){levelQueue.addLast(temp.val);}else{levelQueue.addFirst(temp.val);}if(temp.left != null){TreeQueue.offer(temp.left);}if(temp.right != null){TreeQueue.offer(temp.right);}}res.add(new ArrayList<Integer>(levelQueue));isOrderLeft = !isOrderLeft;}return res;}
    

2.4 N 叉树的层序遍历

  • LeetCode429 给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。树的序列化输入是用层序遍历,每组子节点都由 null值分隔(参见示例)。

  • 输入:root = [ 1, null, 3, 2, 4, null, 5, 6](表述树的元素是这个序列) 输出:[ [1] ,[3,2,4],[5,61] ]
  • N 叉树的定义如下,就是一个值,加一个列表。

  •   public class Node {public int val;public List<Node> children;}
    
  •   public List<List<Integer>> nLevelOrder(Node root){if(root == null){return new ArrayList<List<Integer>>();}List<List<Integer>> res = new ArrayList<>();Deque<Node> NTreeQueue = new ArrayDeque<>();NTreeQueue.offer(root);while(!NTreeQueue.isEmpty()){List<Integer> levelList = new ArrayList<>();while(!NTreeQueue.isEmpty()){Node cur = NTreeQueue.pollFirst();levelList.add(cur.val);for (Node child : cur.children) {if(child != null){NTreeQueue.add(child);}}}res.add(levelList);}return res;}
    

3 几个处理每层元素的项目

  • LeetCode三道题目:515.在每个树行中找最大值(最小),637.二叉树的层平均值,199.二叉树的右视图。

3.1 在每个树行中找最大值

  • LeetCode515: 给定一棵二叉树的根节点 root,请找出该二叉树中每一层的最大值。

  • 在得到一层的元素之后,用一个变量记录其最大值。

  •   public List<Integer> largestValues(TreeNode root){if(root == null){return new ArrayList<Integer>();}List<Integer> res = new ArrayList<>();Deque<TreeNode> TreeQueue = new ArrayDeque<>();//将根节点放入队列中,然后不断遍历TreeQueue.offer(root);while(!TreeQueue.isEmpty()){//存储每一层的元素int levelMaxNum = Integer.MIN_VALUE;for(int i = 0; i< TreeQueue.size(); i++){TreeNode temp = TreeQueue.poll();levelMaxNum = Math.max(levelMaxNum, temp.val);if(temp.left != null){TreeQueue.offer(temp.left);}if(temp.right != null){TreeQueue.offer(temp.right);}}res.add(levelMaxNum);}return res;}
    

3.2 在每个树行中找平均值

  • LeetCode637:要求给定一个非空二叉树,返回一个由每层节点平均值组成的数组。

  • 将每层的元素都先保存下来,最后求平均值。

  •   public List<Double> averageOfLevels(TreeNode root){if(root == null){return new ArrayList<Double>();}List<Double> res = new ArrayList<>();Deque<TreeNode> TreeQueue = new ArrayDeque<>();//将根节点放入队列中,然后不断遍历TreeQueue.offer(root);while(!TreeQueue.isEmpty()){double levelAverage = 0;int len = TreeQueue.size();for(int i = 0; i< len; i++){TreeNode temp = TreeQueue.poll();levelAverage += temp.val;if(temp.left != null){TreeQueue.offer(temp.left);}if(temp.right != null){TreeQueue.offer(temp.right);}}res.add(levelAverage/len);}return res;}
    

3.3 二叉树的右视图

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

  • 利用BFS进行层次遍历,记录下每层的最后一个元素。

  •   public List<Integer> rightSideView(TreeNode root){if(root == null){return new ArrayList<Integer>();}List<Integer> res = new ArrayList<>();Deque<TreeNode> TreeQueue = new ArrayDeque<>();//将根节点放入队列中,然后不断遍历TreeQueue.offer(root);while(!TreeQueue.isEmpty()){int len = TreeQueue.size();int rightValue = 0;for(int i = 0; i< len; i++){TreeNode temp = TreeQueue.poll();if(i == len-1){rightValue = temp.val;}if(temp.left != null){TreeQueue.offer(temp.left);}if(temp.right != null){TreeQueue.offer(temp.right);}}res.add(rightValue);}return res;}
    

3.4 最底层最左边

  • LeetCode513: 给定一个二叉树的 根节点root,请找出该二叉树的 最底层 最左边 节点的值。

  • 假设二叉树中至少有一个节点。

  • 示例1:
    输入:root = [2, 1, 3] 输出:1
    示例2:
    输入:[1, 2, 3, 4, null, 5, 6, null, null, 7] 输出:7
  • 这里有两个问题:该怎么知道什么时候到了最底层呢?假如最底层有两个,该怎么知道哪个是最左的呢?我们继续观察层次遍历的执行过程:

  • 我们可以发现,正常执行层次遍历,不管最底层有几个元素,最后一个输出的一定是是最底层最右的元素7,那这里我们就想了,能否将该处理与上一次题的翻转结合一下,每一层都是先反转再放入队列,就可以让最后一个输出的是最左的呢?是的,这就是解决本题的关键。

  •   public int findBottomLeftValue(TreeNode root){if(root.right == null && root.left == null){return root.val;}Deque<TreeNode> TreeQueue = new ArrayDeque<>();TreeQueue.offer(root);TreeNode temp = new TreeNode();while(!TreeQueue.isEmpty()){int len = TreeQueue.size();for(int i = 0; i< len; i++){temp = TreeQueue.poll();//右节点先入队if(temp.right != null){TreeQueue.offer(temp.right);}//左节点后入队if(temp.left != null){TreeQueue.offer(temp.left);}}}return temp.val;}
    
  • 下面这个没有上面简洁,而且相比之下增加了一些额外的开销,仅供参考:

  •   public int findBottomLeftValue2(TreeNode root){if(root.right == null && root.left == null){return root.val;}Deque<TreeNode> TreeQueue = new ArrayDeque<>();TreeQueue.offer(root);int res = 0;while(!TreeQueue.isEmpty()){int len = TreeQueue.size();Deque<TreeNode> queue = new ArrayDeque<>();for(int i = 0; i< len; i++){TreeNode temp = TreeQueue.poll();queue.addFirst(temp);if(temp.left != null){TreeQueue.offer(temp.left);}if(temp.right != null){TreeQueue.offer(temp.right);}}res = queue.removeLast().val;}return res;}
    

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

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

相关文章

stable diffusion 提示词进阶语法-年龄身材肤色-学习小结

stable diffusion 提示词进阶语法-年龄&身材&肤色 前言年龄提示词青年&#xff08;18-25岁&#xff09;幼年、少年&#xff08;1-18&#xff09;中年&#xff08;35-60岁&#xff09;老年&#xff08;65-80岁 老爷爷 老奶奶&#xff09; 身材提示词肤色关键词(人物基础…

Ubuntu 22.04安装Python3.10.13

Ubuntu最好设置为英文&#xff0c;我之前用中文在make的test的时候&#xff0c;总是会有fail。 查了下有人怀疑是language的问题&#xff0c;保险起见都用英文&#xff0c;个人实践也证明改为英文就不报错了。 issue 44031: test_embed and test_tabnanny fails if the curre…

删除了几个月的照片能找回么 怎么找回半年前删除的照片 数据恢复软件推荐

照片定格了风景&#xff0c;保存了记忆&#xff0c;是我们对过往的一种留念。在数字化的今天&#xff0c;我们越来越习惯把照片存放在电脑里面&#xff0c;随着占用的空间越来越大&#xff0c;我们也会经常处理不用的照片。当然&#xff0c;难免有时候也会误删有用的照片。本篇…

STM32 AD单通道函数设计

单片机学习&#xff01; 目录 文章目录 前言 一、ADC配置步骤 二、详细步骤 2.1 开启RCC时钟 2.2 配置GPIO 2.3 配置多路开关 2.4 配置ADC转换器 2.5 开启ADC电源 2.6 ADC进行校准 2.6.1 复位校准 2.6.2 等待复位校准完成 2.6.3 开始校准 2.6.4 等待校准完成 三、启动AD转换函数…

STM32不使用中断实现定时器微秒级精确延时

我们在写代码的时候避免不了要使用延时函数&#xff0c;很多延时函数都是使用中断或者tick来实现的&#xff0c;tick的方式最大到毫秒ms级别&#xff0c;通过中断方式的通用定时器来实现&#xff0c;如果实现1us的延时那么每1us就来一次中断&#xff0c;很影响cpu的效率。 本文…

程序员表白

啥&#xff1f;&#xff01;你说程序员老实&#xff0c;认真工作&#xff0c;根本不会什么表白&#xff01;那你就错了&#xff01;(除了我) 那今天我们就来讲一下这几个代码&#xff01;赶紧复制下来&#xff0c;这些代码肯定有你有用的时候&#xff01; 1.Python爱心代码 im…

Google XSS Game Level 6 通关方式

文章目录 链接&#xff1a;[Google XSS Game](#https://xss-game.appspot.com/)Level 6 - Follow the &#x1f407;思路1 &#xff08;当然&#xff0c;我使用这个方式没有成功&#xff0c;所以才来记录下&#xff09;解法2 【最简单的解法】需要注意的一个小问题 链接&#x…

单相桥式全控整流电路

1仿真目的 通过对单相桥式全控整流电路的仿真研究&#xff0c;分析电路带电阻负载与阻感负载的不同工作情况。研究对电路的影响 2仿真原理 2.1单相桥式 如图所示为单相桥式全控电路的框图&#xff0c;设负载为电阻负载。在桥式逆变电路中&#xff0c;桥臂的上下两个开关器件…

RabbitMQ在Java中使用 SpringBoot 从基础到高级

充分利用每一个监听者 需要充分利用每一个消费者&#xff0c;需要在配置文件中加上prefetch配置并设置为1 rabbitmq:listener:simple:prefetch: 1 # 每次只能获取一条消息&#xff0c;处理完成才能获取下一个消息创建交换机和队列 创建队列 "fanout.queue1"&…

超融合服务器:企业转型的助推器?

在当今快速发展的数字化时代&#xff0c;企业需要灵活、高效且可靠的IT基础设施来支撑其业务运营。传统的存储环境由于其复杂性和局限性&#xff0c;已经难以满足现代企业的需求。而超融合设备的出现&#xff0c;为企业提供了一个全新的选择。本文将深入探讨超融合服务器的优势…

C++一维数组练习oj(2)

这时上次的C一维数组练习&#xff1a;C一维数组练习oj-CSDN博客 这到题目我承认非常难&#xff01;当然这只是我认为&#xff0c;因为我只学到了一维数组&#xff01; 对于你们来说可能不难。 好了我不客套了。 这题我们可以将他理解为一条时间轴&#xff1a; 时间轴上小李每1…

【Linux 08】进程概念

文章目录 &#x1f308; 01. 基本概念&#x1f308; 02. 描述进程 PCB&#x1f308; 03. 使用 ./ 的方式创建进程&#x1f308; 04. ps 查看进程&#x1f308; 05. getpid / getppid 获取进程标识符&#x1f308; 06. kill 终止指定进程&#x1f308; 07. fork 创建子进程&…

tftp使用

下载 sudo apt-get install tftpd-hpa 创建文件夹 mkdir /home/ljl/work/tftpd mkdir /home/ljl/tftpd chmod 777 tftpd/编辑 sudo vim /etc/default/tftpd-hpa //服务器端 sudo apt-get install tftp-hpa //客户端编辑权限 sudo vi /etc/default/tftpd-hpa 内容&#xff1…

SQL Server 2008R2 日志文件大小设置及查询

SQL Server 2008R2 建立数据库存在日志无限增长问题&#xff0c;造成磁盘内存不足。本文解决这个问题&#xff0c;如下&#xff1a; 1.设置日志文件的最大大小 USE master; GO ALTER DATABASE [D_total] MODIFY FILE (NAME D_total_log, -- 日志文件的逻辑名称MAXSIZE 200…

【黄啊码】如何用GPT和向量数据库做问答型AI

知识库服务依赖该数据库&#xff0c;Embedding 形式个性化训练 ChatGPT&#xff0c;必不可少的就是向量数据库 因为 qdrant 向量数据库只支持 Docker 部署&#xff0c;所以需要先安装好 Docker 服务。 命令行安装 拉取镜像 docker pull qdrant/qdrant 运行服务 docker run -…

飞桨AI应用@riscv OpenKylin

在riscv编译安装飞桨PaddlePaddle参见&#xff1a; 算能RISC-V通用云编译飞桨paddlepaddleopenKylin留档_在riscv下进行paddlelite源码编译-CSDN博客 安装好飞桨&#xff0c;就可以用飞桨进行推理了。刚开始计划用ONNX推理&#xff0c;但是在算能云没有装上&#xff0c;所以最…

【正点原子Linux连载】第十七章 异步通知实验 摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

1&#xff09;实验平台&#xff1a;正点原子ATK-DLRK3568开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id731866264428 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/docs/boards/xiaoxitongban 第十七…

Java发送请求-get源码

发送请求 配置依赖-pom.xml Welcome! - The Apache HTTP Server Project 官网解释这个源码httpClient是执行httpget和httppost 步骤&#xff1a; 查看httpClient源码&#xff0c;源码和方法都没有有用的解释 查看CloseableHttpClient源码类 这个抽象类实现2个接口&#xf…

docker 哲学 - 网络桥接器、容器网络接口 、容器间的通信方式

1、解释 docker0 veth eth 2、vethXX 和 ethXX 是肯定一一对应吗 比如 eth1 对应 veth1 3、如果 A容器使用 默认创建方式 。定义他内部网络为 eth0&#xff0c;容器B使用 --network 连上 已创建的网络 172.89.2.1 。此时假设 B的 ip是 172.89.2.2 &#xff0c;容器网络接口是 e…

2024年企业级通用人工智能的关键技术趋势

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…