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

news2024/9/28 3:22:15

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


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.coloradmin.cn/o/1538658.html

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

相关文章

阿里云OSS分布式存储

目录 &#x1f9c2;1.OSS开通 &#x1f32d;2.头像上传整合OSS &#x1f68d;2.1.引入依赖 &#x1f68d;2.2添加配置 &#x1f68d;2.3创建配置类 &#x1f68d;2.4添加实现类 &#x1f68d;2.5controller调用接口 &#x1f68d;2.6postman测试 1.OSS开通 1.登…

Apache James数据库存储用户信息的密码加密问题

项目场景 Apache James邮件服务器使用数据库来存储用户信息的密码加密问题&#xff1a; 将James的用户改为数据库存储James密码是如何加密验证的 1.将James的用户改为数据库存储 1、修改存储方式 找到james-2.3.2\apps\james\SAR-INF\config.xml 找到<users-store>标…

能被整除的数

给定一个整数 n 和 m个不同的质数 p1,p2,…,pm。 请你求出 1∼n中能被 p1,p2,…,pm 中的至少一个数整除的整数有多少个。 输入格式 第一行包含整数 n 和 m。 第二行包含 m 个质数。 输出格式 输出一个整数&#xff0c;表示满足条件的整数的个数。 数据范围 1≤n≤10^5 …

30V转5V 1A 30降压12V 1A DCDC低电压恒压IC 车充芯片-H4110

30V转5V和30V转12V的DCDC低电压恒压IC&#xff08;也称为降压恒压芯片或车充芯片&#xff09;工作原理如下&#xff1a; 输入电压识别&#xff1a;芯片首先识别输入的30V电压&#xff0c;并准备进行转换。 PWM控制&#xff1a;芯片内部的控制逻辑生成PWM信号。这个信号用于控制…

6个免费的ChatGPT网站

AI 大模型的出现给时代带来了深远的影响&#xff1a; 改变了产业格局&#xff1a;AI 大模型的发展推动了人工智能技术在各行业的广泛应用&#xff0c;改变了传统产业的运作方式&#xff0c;促进了新兴产业的崛起&#xff0c;如智能驾驶、医疗健康、金融科技等。提升了科学研究…

2024-3-22-Qtday3作业

1> 思维导图 2> 要求&#xff1a; 使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否…

家用路由器和企业路由器的区别?

一、家用路由器 家用路由器路由器交换机 它只有一个WAN口和一个LAN口&#xff0c;WAN口接公网一个地址&#xff0c;LAN口接你电脑一个IP地址&#xff0c;完全符合路由器的设计&#xff0c;而因为家里如果用了&#xff0c;说明要接多个电脑&#xff0c;那么如果还需要对每个接口…

Web核心简介

简介 web&#xff1a;全球广域网&#xff0c;也称万维网(www)&#xff0c;能够通过浏览器访问的网站 JavaWeb&#xff1a;是用Java技术来解决相关web互联网领域的技术栈 JavaWeb技术栈 B/S架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式&#xff0c;它的…

2024年超声波清洗机品牌哪家好?实力担当超声波清洗机大集合

随着佩戴眼镜人群越来越多&#xff0c;眼镜清洗的需求也是越来越大了&#xff01;也许有人佩戴了十几年眼镜都不知道超声波清洗机是要清洗的&#xff0c;也许有人一开始就注重眼镜的清洗。其实眼镜清洗是一件很简单的事情&#xff0c;可以用超声波清洗机来清洗眼镜。目前超声波…

LeetCode Python - 70. 爬楼梯

目录 题目描述解法方法一&#xff1a;递推方法二&#xff1a;矩阵快速幂加速递推方法三 运行结果方法一方法二方法三 题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1…

伊理威科技:抖音开网店新手刚做选啥品

在数字浪潮中&#xff0c;抖音不仅是展示才艺的舞台&#xff0c;更是创业者的新天地。新手若想在这片热土上开垦网店&#xff0c;选品便是首要课题。选择产品如同种下希望的种子&#xff0c;既要考量土壤肥沃度&#xff0c;也得预测风雨适宜期。 兴趣与专长是选品的罗盘。热爱所…

Data Interpreter: An LLM Agent For Data Science 论文解读

论文地址&#xff1a;https://arxiv.org/abs/2402.18679 Github&#xff1a;MetaGPT: The Multi-Agent Framework 数据解释器&#xff08;Data Interpreter&#xff09;是一个基于大型语言模型&#xff08;LLM&#xff09;的代理&#xff0c;专门为解决数据科学问题而设计。它…

Flume详解(2)

Flume Sink HDFS Sink 将数据写到HDFS上。数据以文件形式落地到HDFS上&#xff0c;默认是以FlumeData开头&#xff0c;可以通过hdfs.filePrefix来修改 HDFS Sink默认每隔30s会滚动一次生成一个文件&#xff0c;因此会导致在HDFS上生成大量的小文件&#xff0c;实际过程中&am…

【力扣刷题日记】603.连续空余座位

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 603.连续空余座位 表&#xff1a;Cinema 列名类型seat_idintfreebool Seat_id 是该表的自动递增主键列。 在…

数学建模(层次分析法 python代码 案例)

目录 介绍&#xff1a; 模板&#xff1a; 例题&#xff1a;从景色、花费、饮食&#xff0c;男女比例四个方面去选取目的地 准则重要性矩阵&#xff1a; 每个准则的方案矩阵&#xff1a;​ 一致性检验&#xff1a; 特征值法求权值&#xff1a; 完整代码&#xff1a; 运行结…

2024最新版pycharm激活码【资源共享】一键复制

好用的话 家人们记得点个赞喔 VPXI6TDKOQ-eyJsaWNlbnNlSWQiOiJWUFhJNlRES09RIiwibGljZW5zZWVOYW1lIjoi5rC45LmF5rA5rS7IHd3d8K3YWppaHVvwrdjb20iLCJsaWNlbnNlZVR5cGUiOiJQRVJTT05BTCIsImFzc2lnbmVlTmFtZSI6IiIsImFzc2lnbmVlRW1haWwiOiIiLCJsaWNlbnNlUmVzdHJpY3Rpb24iOiIiLCJjaG…

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

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

haproxy 高可用

一 haproxy HAProxy简介 HAProxy提供高可用、负载均衡以及基于TCP和HTTP的应用代理&#xff0c;适合处理高负载站点的七层数据请求。类似的代理服务可以屏蔽内部真实服务器&#xff0c;防止内部服务器遭受攻击。 HAProxy特点和优点&#xff1a; 1.支持原声SSL,同时支持客户端和…

【Android 源码】Android源码下载指南

文章目录 前言安装Repo初始化Repo选择分支没有梯子替换为清华源 有梯子 下载源码下载开始参考 前言 这是关于Android源码下载的过程记录。 环境&#xff1a;Windows上通过VMware安装的Ubuntu系统 安装Repo 创建Repo文件目录 mkdir ~/bin PATH~/bin:$PATH下载Repo工具&#…

火哥Windows内核第五期

主要讲解windows的保护模式&#xff0c;系统调试&#xff0c;异常发现及处理等等。事件等待、异常、软件调试、内存管理、消息机制、调试器开发项目、内核工具开发项目&#xff0c;发展方向:CC驱动开发&#xff0c;软件逆向&#xff0c;破解&#xff0c;游戏保护&#xff0c;反…