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

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


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;
          }
    

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

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

相关文章

51单片机入门:定时器

定时器的介绍 定时器&#xff1a;51单片机的定时器属于单片机的内部资源&#xff0c;其电路的设计连接和运转均在单片机内部完成。根据单片机内部的时钟或者外部的脉冲信号对寄存器中的数据加1&#xff0c;定时器实质就是加1计数器。因为又可以定时又可以计数&#xff0c;又称…

禁欲28天!一宅男居然肝出如此详细Web安全学习笔记,学妹看完直接抽搐了!

1.1. Web技术演化 1.1.1. 简单网站 1.1.1.1. 静态页面 Web技术在最初阶段&#xff0c;网站的主要内容是静态的&#xff0c;大多站点托管在ISP上&#xff0c;由文字和图片组成&#xff0c;制作和表现形式也是以表格为主。当时的用户行为也非常简单&#xff0c;基本只是浏览网…

51单片机—直流电机

1.元件介绍 2.驱动电路 3.电机调速 一般会保证一个周期的时间是一样的 应用&#xff1a; 1.LED呼吸灯 #include <REGX52.H>sbit LEDP2^0;void Delay(unsigned int t) {while(t--); } void main() {unsigned char Time,i;while(1){for(Time0;Time<100;Time){for(i0;…

Linux相关命令(1)

1、找出文件夹下包含 “aaa” 同时不包含 “bbb”的文件&#xff0c;然后把他们重新生成一下。要求只能用一行命令。 find ./ -type f -name "*aaa*" ! -name "*bbb*" -exec touch {} \;文件系统操作命令 df&#xff1a;列出文件系统的整体磁盘使用情况 …

RocketMQ基础知识和常见问题

RocketMQ 是一个 队列模型 的消息中间件&#xff0c;具有高性能、高可靠、高实时、分布式 的特点。它是一个采用 Java 语言开发的分布式的消息系统&#xff0c;由阿里巴巴团队开发&#xff0c;在 2016 年底贡献给 Apache&#xff0c;成为了 Apache 的一个顶级项目。 在阿里内…

Linux 创建交换空间

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

明牌空投:Cosmos生态项目Joltify零撸教程

简介&#xff1a;Joltify Finance 是基于Cosmos SDK的Layer1公链&#xff0c;做RWA赛道的&#xff0c;它可以将加密世界中的大量流动性与现实世界的金融资产合并&#xff0c;将有形资产转换为代币或NFT的过程&#xff0c;使它们能够在链上进行交易&#xff0c;从而在DeFi和传统…

内存卡损坏怎么修复数据,内存卡损坏修复数据方法

内存卡损坏是许多用户都可能面临的问题。当我们的内存卡损坏时,其中存储的重要数据可能会受到威胁,承载着我们无尽回忆的数据,一旦失去,将成为大家心中永远的遗憾。因此我们迫切需要找到一种方法来修复这些数据。本文将介绍一些内存卡损坏修复数据方法,帮助大家解决因为内…

外卖店优先级c++

题目 输入样例&#xff1a; 2 6 6 1 1 5 2 3 1 6 2 2 1 6 2输出样例&#xff1a; 1样例解释 6时刻时&#xff0c;1 号店优先级降到 3&#xff0c;被移除出优先缓存&#xff1b;2 号店优先级升到 6&#xff0c;加入优先缓存。 所以是有 1 家店 (2 号) 在优先缓存中。 思路 …

严平稳随机过程、广义平稳随机过程、各态历经性

严平稳随机过程指的是所有统计特性均与时间起点无关&#xff0c;即时间平移不影响其任何统计特性。工程上解释即可以在任意时间点去测量信号的统计特性&#xff0c;不会因为测量的时间改变而产生影响。 广义平稳随机过程&#xff0c;常常称为平稳过程&#xff0c;指的是均值与自…

makefile编译第一讲

更多精彩内容在公众号。关注公众号&#xff0c;加v&#xff0c;免费送你两本makefile电子书。轻松掌握makefile 在C和C中&#xff0c;首先要把源文件编译成中间代码文件&#xff0c;在windows下就是obj文件&#xff0c;linux下就是.o文件&#xff1a;object file。这个动作叫做…

2.9 什么是A/B测试?如何进行A/B测试?

2.9 什么是A/B测试&#xff1f; 场景描述 在互联网公司中&#xff0c;A/B 测试是验证新模块、新功能、新产品是否有效&#xff0c;新算法、新模型的效果是否有提升&#xff0c;新设计是否受到用户欢迎&#xff0c;新更改是否影响用户体验的主要测试方法。在机器学习领域中&…

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…

C++入门笔记开源【研究生3年+7W字】

博主研究生3年时间积累了一个C的基础知识文档&#xff0c;共计7W字。几乎把常用的各种语法和接口都包含进去了。一个文档&#xff0c;markdown格式的&#xff0c;可以当做工具书来使用。由于本文档内容较多&#xff0c;直接复制到csdn会各种卡&#xff0c;而且图片链接不对&…

全智能深度演进,一键成片让视频创作颠覆式提效

全智能一键成片&#xff0c;让内容创作的「边际成本」逼近于零。 大模型和AIGC技术的发展&#xff0c;可以用“日新月异”来形容&#xff0c;其迭代速度史无前例&#xff0c;涌现出的各类垂直应用模型&#xff0c;也使得音视频行业的应用场景更加广泛和多样化。 然而&#xff…

PTA L2-027 名人堂与代金券

对于在中国大学MOOC&#xff08;http://www.icourse163.org/ &#xff09;学习“数据结构”课程的学生&#xff0c;想要获得一张合格证书&#xff0c;总评成绩必须达到 60 分及以上&#xff0c;并且有另加福利&#xff1a;总评分在 [G, 100] 区间内者&#xff0c;可以得到 50 元…

Unity vision pro模拟器开发教程-附常见问题解决方案

前言 庄生晓梦迷蝴蝶&#xff0c;望帝春心托杜鹃 废话 去年苹果发布会上&#xff0c;推出了Vision Pro这一款XR产品。并且宣布Unity作为其主要合作伙伴&#xff0c;负责开发XR的开发产品。 这消息一出&#xff0c;当晚Unity的股价直接被熔断。产品发布之后&#xff0c;一直等…

java篇 让java对象具有链式调用

一 操作 1.1 流程 1.在类中引入注解Accessors(chain true)&#xff0c;引入后&#xff0c;不要在使用自定义的getter&#xff0c;setter方法 Data Accessors(chain true) public class Student {private String name;private int age;Overridepublic String toString() {r…

【模板】AcWing873. 《欧拉函数》(C++)

【题目描述】 给定 n 个正整数 &#xff0c;请你求出每个数的欧拉函数。 欧拉函数的定义 【输入格式】 第一行包含整数 n。 接下来 n 行&#xff0c;每行包含一个正整数 。 【输出格式】 输出共 n 行&#xff0c;每行输出一个正整数 的欧拉函数。 【数据范围】 1≤n≤1…

opencv 十八 python下实现0缓存掉线重连的rtsp直播流播放器

使用opencv打开rtsp视频流时&#xff0c;会因为网络问题导致VideoCapture掉线&#xff1b;也会因为图像的后处理阶段耗时过长导致opencv缓冲区数据堆积&#xff0c;从而使程序无法及时处理最新的数据。为此对cv2.VideoCapture进行封装&#xff0c;实现0缓存掉线重连的rtsp直播流…