树专题 —— 二叉树层序遍历

大家好,我是 方圆。本篇关于二叉树的层序遍历,主要以题目为主,而且我觉得层序遍历是求解二叉树问题中最简单的,学会了基本的层序遍历,在这基础上的扩展题也能迎刃而解,如果大家想要找刷题路线的话,可以参考 Github: LeetCode

层序遍历

我们可以一起先看一下 102. 二叉树的层序遍历,本题是最基本的层序遍历。我先把题解放在这里,之后再具体的解释下:

    public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> res = new LinkedList<>();if (root == null) {return res;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {int size = queue.size();List<Integer> element = new LinkedList<>();for (int i = 0; i < size; i++) {TreeNode node = queue.poll();element.add(node.val);if (node.left != null) {queue.offer(node.left);}if (node.right != null) {queue.offer(node.right);}}res.add(element);}return res;}

层序遍历顾名思义即逐层进行遍历,先将每层的节点存在 队列 当中,然后进行出队(取出当前层节点)和入队(存入下一层的节点)的操作,以此达到遍历的目的。

简单练习
  • 111. 二叉树的最小深度

二叉树的最小深度即找到最早出现的叶子节点,我们可以借助层序遍历每层每层地去找,一旦发现左右子树均为空的节点返回当前深度即可,题解如下:

    public int minDepth(TreeNode root) {int res = 0;if (root == null) {return res;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {res++;int size = queue.size();for (int i = 0; i < size; i++) {TreeNode node = queue.poll();if (node.left == null && node.right == null) {return res;}if (node.left != null) {queue.offer(node.left);}if (node.right != null) {queue.offer(node.right);}}}return res;}
  • LCR 151. 彩灯装饰记录 III

本题是非常直观的层序遍历变体,每层记录答案时使用链表,不断地变换头插法和尾插法即可,题解如下:

    public List<List<Integer>> decorateRecord(TreeNode root) {List<List<Integer>> res = new LinkedList<>();if (root == null) {return res;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);boolean left = true;while (!queue.isEmpty()) {int size = queue.size();LinkedList<Integer> element = new LinkedList<>();for (int i = 0; i < size; i++) {TreeNode node = queue.poll();if (left) {element.addLast(node.val);} else {element.addFirst(node.val);}if (node.left != null) {queue.offer(node.left);}if (node.right != null) {queue.offer(node.right);}}left = !left;res.add(element);}return res;}
  • 199. 二叉树的右视图

本题就更简单了,遍历每层时取该层的最右端元素即可,题解如下:

    public List<Integer> rightSideView(TreeNode root) {List<Integer> res = new LinkedList<>();if (root == null) {return res;}LinkedList<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {res.add(queue.peekLast().val);int size = queue.size();for (int i = 0; i < size; i++) {TreeNode node = queue.poll();if (node.left != null) {queue.offer(node.left);}if (node.right != null) {queue.offer(node.right);}}}return res;}
进阶练习

接下来我们来看一些处理相对复杂的练习,但是它们的本质都是二叉树的层序遍历

  • 623. 在二叉树中增加一行

在二叉树中添加一行,我觉得这道题的难点在于理解在插入这一行后节点间的引用关系该如何分配:

  • 如果在 depth 为 1 时插入一行,这种为特殊情况,会改变根节点,需要特殊处理

  • 如果在 depth > 1 的情况下插入一行,根节点引用是不变的,找到对应行之后,为所有节点添加值为 val 的左右子树,并将原来的左子树拼接在新左子树的左子树上,将原来的右子树拼接在新右子树的右子树上

    public TreeNode addOneRow(TreeNode root, int val, int depth) {if (depth == 1) {TreeNode node = new TreeNode(val);node.left = root;return node;}Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);int curDepth = 2;while (!queue.isEmpty()) {if (curDepth == depth) {while (!queue.isEmpty()) {TreeNode node = queue.poll();TreeNode left = new TreeNode(val);left.left = node.left;node.left = left;TreeNode right = new TreeNode(val);right.right = node.right;node.right = right;}break;}curDepth++;int size = queue.size();for (int i = 0; i < size; i++) {TreeNode node = queue.poll();if (node.left != null) {queue.offer(node.left);}if (node.right != null) {queue.offer(node.right);}}}return root;}
  • 655. 输出二叉树

本题在思路上不难,只是对应树节点索引值计算需要注意一下,为了求解方便,我们创建了 TreeNodeIndex 辅助记录节点和节点的索引信息。由于根节点的索引位置需要二叉树的高度作为参数才能计算出来,所以我们需要先求二叉树的高度,之后便是简单的层序遍历,在这个过程中只需要注意索引值的计算即可,题解如下:

    static class TreeNodeIndex {TreeNode node;int m;int n;public TreeNodeIndex(TreeNode node, int m, int n) {this.node = node;this.m = m;this.n = n;}}public List<List<String>> printTree(TreeNode root) {List<List<String>> res = new LinkedList<>();int height = depth(root) - 1;int n = (int) (Math.pow(2, height + 1) - 1);ArrayList<String> element = new ArrayList<>();for (int i = 0; i < n; i++) {element.add("");}LinkedList<TreeNodeIndex> queue = new LinkedList<>();queue.offer(new TreeNodeIndex(root, 0, (n - 1) / 2));while (!queue.isEmpty()) {int size = queue.size();ArrayList<String> e = (ArrayList<String>) element.clone();for (int i = 0; i < size; i++) {TreeNodeIndex node = queue.poll();e.set(node.n, String.valueOf(node.node.val));int commonValue = (int) Math.pow(2, height - node.m - 1);if (node.node.left != null) {queue.offer(new TreeNodeIndex(node.node.left, node.m + 1, node.n - commonValue));}if (node.node.right != null) {queue.offer(new TreeNodeIndex(node.node.right, node.m + 1, node.n + commonValue));}}res.add(e);}return res;}private int depth(TreeNode root) {if (root == null) {return 0;}int left = depth(root.left);int right = depth(root.right);return Math.max(left, right) + 1;}
  • 987. 二叉树的垂序遍历

垂序遍历的思路并不难,大家需要做的是借助层序遍历计算所有节点的索引,根据题目的排序规则将所有的节点加入到优先队列中,之后按照同一列为一组元素组合成答案即可,题解如下:

    static class TreeNodeIndex implements Comparable<TreeNodeIndex> {int x;int y;TreeNode node;public TreeNodeIndex(int x, int y, TreeNode node) {this.x = x;this.y = y;this.node = node;}@Overridepublic int compareTo(TreeNodeIndex o) {if (this.y == o.y) {if (this.x == o.x) {return this.node.val - o.node.val;} else {return this.x - o.x;}} else {return this.y - o.y;}}}public List<List<Integer>> verticalTraversal(TreeNode root) {TreeNodeIndex rootNode = new TreeNodeIndex(0, 0, root);Queue<TreeNodeIndex> queue = new LinkedList<>();queue.offer(rootNode);PriorityQueue<TreeNodeIndex> priorityQueue = new PriorityQueue<>();priorityQueue.offer(rootNode);while (!queue.isEmpty()) {int size = queue.size();for (int i = 0; i < size; i++) {TreeNodeIndex node = queue.poll();if (node.node.left != null) {TreeNodeIndex left = new TreeNodeIndex(node.x + 1, node.y - 1, node.node.left);priorityQueue.offer(left);queue.offer(left);}if (node.node.right != null) {TreeNodeIndex right = new TreeNodeIndex(node.x + 1, node.y + 1, node.node.right);priorityQueue.offer(right);queue.offer(right);}}}List<List<Integer>> res = new LinkedList<>();while (!priorityQueue.isEmpty()) {TreeNodeIndex peek = priorityQueue.peek();LinkedList<Integer> element = new LinkedList<>();while (!priorityQueue.isEmpty() && priorityQueue.peek().y == peek.y) {element.add(priorityQueue.poll().node.val);}res.add(element);}return res;}
  • 297. 二叉树的序列化与反序列化

之前我们写过 449. 序列化和反序列化二叉搜索树 中等,因为二叉搜索树节点间有大小关系能够区分去根节点和左右子树,所以它的序列化思路和普通二叉树序列化思路并不相同。普通二叉树的序列化和反序列化可以层序遍历来实现,与我们平时写的层序遍历不同的是:我们需要将其中所有的非空节点也记录下来,这样我们才能确定每个节点所在的合适的位置,它并不是很难,参考代码如下:

    // Encodes a tree to a single string.public String serialize(TreeNode root) {if (root == null) {return "";}StringBuilder res = new StringBuilder();Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {int size = queue.size();for (int i = 0; i < size; i++) {TreeNode node = queue.poll();if (node == null) {res.append("null").append(",");} else {res.append(node.val).append(",");queue.offer(node.left);queue.offer(node.right);}}}return res.toString();}// Decodes your encoded data to tree.public TreeNode deserialize(String data) {if ("".equals(data)) {return null;}String[] nodes = data.split(",");TreeNode root = new TreeNode(Integer.parseInt(nodes[0]));Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);int index = 1;while (index < nodes.length) {int size = queue.size();for (int i = 0; i < size; i++) {TreeNode node = queue.poll();if ("null".equals(nodes[index])) {node.left = null;} else {TreeNode left = new TreeNode(Integer.parseInt(nodes[index]));node.left = left;queue.offer(left);}index++;if ("null".equals(nodes[index])) {node.right = null;} else {TreeNode right = new TreeNode(Integer.parseInt(nodes[index]));node.right = right;queue.offer(right);}index++;}}return root;}

巨人的肩膀

  • 菜鸟教程 - 二叉树的层序遍历

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

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

相关文章

希宝猫罐头怎么样?专业人士告诉你质量好又便宜的猫罐头推荐

作为从业6年的宠物护理师来说&#xff0c;只买合适的&#xff0c;贵的不如好的&#xff0c;只要配方不出错营养跟得上&#xff0c;观察自家猫咪体质真的基本不怎么出错。希望大家看完这篇文章&#xff0c;各位铲屎官都能买到满意的猫罐头。那么希宝猫罐头在各方面表现怎么样呢&…

UI咨询公司-蓝蓝设计:顶级秘籍:提升UI设计吸引力的3大绝招

想要让你的UI设计在海量应用中脱颖而出&#xff0c;吸引用户眼球吗&#xff1f;如果你正在寻找提升UI设计吸引力的绝妙方法&#xff0c;那么你绝对不能错过本文&#xff01;我们将为你揭示顶级UI设计师都不会告诉你的3大绝招&#xff0c;让你轻松掌握提升UI设计吸引力的关键技巧…

网络运维与网络安全 学习笔记2023.11.30

网络运维与网络安全 学习笔记 第三十一天 今日目标 实现AP自动注册、配置WLAN业务参数、无线终端通过wifi互访 实现AP自动注册 项目背景 企业内网的大量AP已经通过DHCP的方式获得IP地址 为了实现后期大量AP的统一管理&#xff0c;希望通过AC实现集中控制 在AC设备上&#…

c 语言常见的易错题分析

在C语言中&#xff0c;有一些常见的易错题需要特别注意。以下是一些例子&#xff1a; 1&#xff0c;数组越界&#xff1a;在C语言中&#xff0c;数组的索引是从0开始的。因此&#xff0c;一个长度为n的数组的索引范围应该是0到n-1。如果尝试访问超出这个范围的索引&#xff0c…

SpringBoot框架结合Redis实现分布式锁

一、SpringBoot结合 Redis实现分布式锁 1.1、什么是分布式锁 分布式锁&#xff0c;是在分布式的环境下&#xff0c;才会使用到的一种同步访问机制&#xff0c;在传统的单体环境里面&#xff0c;不存在分布式锁的概念&#xff0c;只有在分布式环境里面&#xff0c;才有分布式锁…

赴日程序员高年薪过上“躺平”生活?

日本的IT行业想要达到的高薪&#xff0c;也是需要很多资历和经验的&#xff0c;不过即使你是新卒&#xff0c;也能拿到相比国内来说让你满意的薪资。 刚入职的起薪是20-23万日元/月&#xff0c;情报信息业出身&#xff0c;技术掌握不错&#xff0c;起薪是25万-30万日元。之后经…

git的安装及ssh配置(Linux)

环境 CentOS Linux release 7.9.2009 (Core) Xftp7 安装 方法一&#xff1a;yum安装 yum是一个客户端软件&#xff0c;就好比手机上的应用商店&#xff0c;帮助我们对软件的下载、安装和卸载 1、首先查看自己是否安装过git [rootxiaoxi ~]# git -bash: git: command not fo…

C++继承(详解)

一、继承的概念 1.1、继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承呈现了面向对象程序设计的层次结…

【JavaEE】单例模式

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文于《JavaEE》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力)打造&…

linux下开机小助手一个开机实现诸多功能的小脚本

linux下开机小助手一个开机实现诸多功能的小脚本 今天来分享一个开机小助手&#xff0c;效果如下 00:17:31 up 1:53, 3 users, load average: 0.00, 0.01, 0.02total used free shared buff/cache available Mem: 3931 288 …

Python容器——字典

Key——Value 键值对

科技云报道:AI+PaaS,中国云计算市场迎来新“变量”?

科技云报道原创。 没有小的市场&#xff0c;只有还没有被发现的大生意。 随着企业数字化转型的逐级深入&#xff0c;市场需求进一步向PaaS和SaaS层进发&#xff0c;使之成为公有云服务市场增长的主要动力。 根据IDC最新发布的报告显示&#xff0c;2022-2027五年间中国公有云…

【MODBUS】Modbus协议入门简介

Modbus&#xff08;Modicon Communication Protocol&#xff09;是一种用于工业自动化领域的通信协议&#xff0c;最初由Modicon&#xff08;现在是施耐德电气的一部分&#xff09;开发。Modbus协议被广泛应用于连接不同厂商的工业设备&#xff0c;实现设备之间的通信和数据交换…

初识计算机网络

网络通信基础 1. IP地址2.端口号3.认识协议3.1协议分层 4. 网络数据传输的基本流程4.1 五元组4.2封装和分用 1. IP地址 IP地址主要用于表示网络主机,其他网络设备的网络地址,IP地址用于定位主机的网络地址 比如:发送快递的时候,需要知道对象的收货地址,才能将包裹送到目的地. …

APISpace 实名认证(身份证二要素)接口案例代码

1.实名认证&#xff08;身份证二要素&#xff09;API APISpace 的 实名认证&#xff08;身份证二要素API&#xff09;&#xff0c;核验身份证二要素&#xff08;姓名和身份证号码&#xff09;信息是否一致。 2.实名认证&#xff08;身份证二要素&#xff09;接口详情 2.1 接口…

MongoDB日期查询详解

MongoDB日期查询详解 一、MongoDB日期查询格式 MongoDB中日期查询格式采用ISODate()函数加上日期字符串的形式&#xff0c;如下所示&#xff1a; db.collection.find({create_time:{$gte:ISODate("2021-01-01T00:00:00.000Z")}})其中&#xff0c;gte’表示大于等于…

卡伦特C++ 回忆

线程通信和进程通信方法 进程间 进程间通信&#xff08;英语&#xff1a;Inter-Process Communication&#xff0c;简称IPC&#xff09;&#xff0c;指至少两个进程或线程间传送数据或信号的一些技术或方法 管道 单向通信&#xff0c;只能在具有亲缘关系的进程之间使用 命…

外汇天眼:CySEC宣布与Titanedge Securities 达成90,000欧元的和解

塞浦路斯证券交易委员会&#xff08;CySEC&#xff09;12月1日宣布已经与塞浦路斯投资公司Titanedge Securities Ltd 达成了一项和解。 此次和解涉及可能违反了2017年《投资服务和活动以及受监管市场法》的情况。更具体地说&#xff0c;达成和解的调查涉及评估该公司在2017/565…

自动化测试的4大注意事项

自动化测试能够提高测试效率、覆盖率&#xff0c;降低测试成本和工作量&#xff0c;是软件开发中不可或缺的一部分。但前提是要确保自动化测试的有效性和可靠性&#xff0c;否则无效或错误的自动化测试&#xff0c;往往会对项目造成负面影响&#xff0c;如维护成本高、假阳性和…

高等职业学校新媒体营销实训室解决方案

背景 随着数字化时代的来临&#xff0c;新媒体营销成为企业推广和品牌建设的关键手段。为了培养高职学生在新媒体领域的实际操作能力&#xff0c;建立一套全面、系统的实训室方案至关重要。 目标 搭建高职新媒体营销实训室&#xff0c;旨在培养学生的实际操作能力&#xff0…