当前位置: 首页 > news >正文

【代码随想录训练营】【Day15】第六章|二叉树|层序遍历|226.翻转二叉树|101.对称二叉树

层序遍历

题目详细:LeetCode.102

层序遍历与上一节讲的三种遍历方式有所不同,层序遍历是指按从上到下,从左到右的顺序,逐层地遍历二叉树的节点。

从其节点的遍历顺序上观察,我们可以发现其跟广度优先遍历(BFS)的遍历思想非常相似,既然我们可以利用队列先进先出的特点来实现广度优先遍历,那么我们也可以用队列来实现对二叉树的层序遍历:

  • 定义一个二维数组,其下标表示第i层,数组元素则存储着每一层遍历的节点
  • 定义一个队列,利用其先进先出的特点,先将非空的根节点进队
  • 定义一个循环,遍历入队的二叉树节点
    • 定义一个变量,记录每一层的节点数目,也就是当前队列的长度
    • 定义一个循环,获取变量得知在第一层,只有根节点一个节点
      • 那么我们只需要出队一次,将根节点出队,队列长度 - 1
      • 定义一个一维数组存放该层的节点值,并将该数组追加入到二维数组中
      • 然后将其非空的左右节点依次进队
      • 循环直到该层的节点都遍历完毕
  • 循环直到队列为空,说明二叉树层序遍历完成

Java解法(队列,BFS):

class Solution {public List<List<Integer>> ans = new ArrayList<>();public List<List<Integer>> levelOrder(TreeNode root) {this.bfs(root);return ans;}public void bfs(TreeNode root){Queue<TreeNode> queue = new LinkedList<>();if(null != root) queue.offer(root);while(!queue.isEmpty()){int n = queue.size();List<Integer> floor = new ArrayList<>();while(n-- > 0){TreeNode node = queue.poll();floor.add(node.val);if(null != node.left) queue.offer(node.left);if(null != node.right) queue.offer(node.right);}ans.add(floor);}}
}

这道题我们可以模拟层序遍历的思想,利用递归来解题,只需要在递归函数中增加一个变量,记录节点是第几层的即可:

Java解法(模拟,递归):

class Solution {public List<List<Integer>> ans = new ArrayList<>();public List<List<Integer>> levelOrder(TreeNode root) {this.order(root, 0);return ans;}public void order(TreeNode root, int level){if(null == root) return;if(level > ans.size() - 1){List<Integer> floor = new ArrayList<>();floor.add(root.val);ans.add(floor);}else{ans.get(level).add(root.val);}this.order(root.left, level + 1); this.order(root.right, level + 1); }
}

学会了二叉树的层序遍历后,可以一口气打完以下十道LeetCode题:

102.二叉树的层序遍历
104.二叉树的最大深度
107.二叉树的层次遍历II
111.二叉树的最小深度
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针II
199.二叉树的右视图
429.N叉树的层序遍历
515.在每个树行中找最大值
637.二叉树的层平均值


翻转二叉树

题目详细:LeetCode.226

翻转二叉树的过程和结果:
在这里插入图片描述
观察上图动画我们可以发现,上图采用的是层序遍历的顺序,在遍历的过程中,将每个节点的左右节点进行交换,这就是翻转二叉树的解题思想。

所以想要解题很简单,就是采用合适的遍历方法,在访问节点的时候,将其左右节点进行交换即可;

需要注意的是,在这道题中我们采用的遍历二叉树方法一般是:前序遍历、后序遍历和层序遍历,因为采用中序遍历的方式会导致重复交换子节点,需要在遍历过程中加以逻辑判断才能避免这一情况,不易编写和理解。

如何遍历在这里就不作赘述了,不了解的可以查看上一节的内容,详细讲述了遍历二叉树的各种方法:【Day14】传送门

Java解题(递归,前序遍历):

class Solution {public TreeNode invertTree(TreeNode root) {this.invertPre(root);return root;}public void swapChildNode(TreeNode node){TreeNode temp = node.left;node.left = node.right;node.right = temp;}public void invertPre(TreeNode root){if(null == root) return;this.swapChildNode(root);this.invertPre(root.left);this.invertPre(root.right);}
}

Java解题(统一迭代,前序遍历):

class Solution {public TreeNode invertTree(TreeNode root) {this.invertPre(root);return root;}public void swapChildNode(TreeNode node){TreeNode temp = node.left;node.left = node.right;node.right = temp;}public void invertPre(TreeNode root){Stack<TreeNode> stack = new Stack<>();if(null != root) stack.push(root);while(!stack.isEmpty()){// 标记节点TreeNode tagNode = stack.peek();if(tagNode != null){// 非标记节点,先弹出,在需要处理的节点后追加空指针作为标记(仅作标记,不作处理)TreeNode node = stack.pop();if(null != node.right) stack.push(node.right);if(null != node.left) stack.push(node.left);stack.push(node);stack.push(null);}else{// 遇到标记节点,先弹出作为标记的空指针的节点,再处理数据(处理节点,不作标记)stack.pop();TreeNode node = stack.pop();this.swapChildNode(node);}}}
}

对称二叉树

题目详细:LeetCode.101

由题可知,判断一棵二叉树是否对称:

  • 根节点无需比较
  • 接着比较树的内侧的节点和外侧的节点,而不是比较树的左右节点
  • 当每棵子树都满足内侧节点相等,外侧节点相等,那么则称这整棵二叉树是对称的

所以关键在于如何遍历二叉树,以及如何比较树的内侧节点和外侧节点。

既然根节点不需要比较,那么我们就需要同时比较其左右两棵子树上的节点,在比较过程中,会出现四种情况:

  • 左右节点皆为空,则是对称的
  • 左右节点只有一个为空,则是不对称的
  • 左右节点皆不为空,但是他们的值不相等,是不对称的
  • 左右节点皆不为空,但是他们的值相等,是对称的

所以对于每一棵子树,我们只需要按照上述情况去比较其内侧和外侧节点,就可以得到正确答案:

  • 树的内侧节点左子树的右节点和右子树的左节点进行比较
  • 树的外侧节点左子树的左节点和右子树的右节点进行比较
  • 当出现一种不对称情况时,则整个树是不对称的,无需继续比较
  • 当出现对称情况时,继续比较剩余的左右子树

Java解题(递归):

class Solution {public boolean isSymmetric(TreeNode root) {return this.check(root.left, root.right);}public boolean check(TreeNode leftNode, TreeNode rightNode){if(null == leftNode && null == rightNode) return true;else if(null == leftNode || null == rightNode || leftNode.val != rightNode.val) return false;return check(leftNode.right, rightNode.left) && check(leftNode.left, rightNode.right);}
}

Java解题(迭代,队列):

class Solution {public Queue<TreeNode> queue = new LinkedList<>();public boolean isSymmetric(TreeNode root) {return this.check(root.left, root.right);}public boolean check(TreeNode leftNode, TreeNode rightNode){this.queue.offer(leftNode);this.queue.offer(rightNode);while(!queue.isEmpty()){leftNode = queue.poll();rightNode = queue.poll();if(null == leftNode && null == rightNode)continue;else if(null == leftNode || null == rightNode || leftNode.val != rightNode.val){return false;        this.queue.offer(leftNode.right);this.queue.offer(rightNode.left);this.queue.offer(leftNode.left);this.queue.offer(rightNode.right);}return true;}
}

观察代码以及二叉树的遍历过程,我们也可以发现,对于对称二叉树的遍历顺序,左子树的遍历顺序是右左根,而右子树的遍历顺序是左右根,两者都属于后序遍历,也就是说,这道题是基于后序遍历来解决的。


除了在二叉树中常用的递归方法外,我们还可以结合前面学习的其他数据结构,如栈和队列,也能够辅助我们遍历和处理二叉树。

最后这两天做二叉树相关的题目之后,给我的感触就是,二叉树的题目千变万化,但是总结起来就是两个要点:选择最佳的遍历顺序 + 处理节点的需求逻辑,可见解答二叉树相关的题目时,理解和掌握二叉树不同的遍历思想是尤其重要的:

水之积也不厚,则其负大舟也无力。

http://www.lryc.cn/news/8006.html

相关文章:

  • 基于圆展开自适应三边测量算法的室内定位
  • 使用中断子系统实现对LED灯的控制
  • 《爆肝整理》保姆级系列教程python接口自动化(十五)--参数关联接口(详解)
  • 【JDK8】MyBatis源码导入Idea
  • 三层交换机DHCP中继
  • C++之RALL机制
  • 回溯算法章末总结
  • 【SpringBoot】为异步任务规划线程池
  • SAP ABAP 输出结果带有空格
  • Opengl ES之踩坑记
  • 设计模式第六讲:责任链模式和迭代器模式详解
  • K8s 架构简介(一)
  • xshell6运行报错:由于找不到mfc110u.dll、MSVCR110.dll无法继续执行代码
  • Baklib知识库管理平台,协助组织提升知识管理水平
  • 一文搞懂core-scheduling核心机制
  • IP地址在金融行业有哪些应用?
  • GT-suite v2016解决许可证过期问题(附新版liscense下载地址)
  • 小红书商业笔记与普通笔记区别是什么?小红书笔记有哪几种
  • DataWhale-统计学习方法打卡Task01
  • Java面试——Spring 事务
  • Python语言零基础入门教程(十九)
  • 重生之我是赏金猎人-SRC漏洞挖掘(一)-某SRC测试系统无脑Getshell
  • Sciter 结合 PReact 实现组件公共逻辑抽离
  • OpenTracing协议规范链接
  • 金三银四面试必看,自动化测试如何解决日志问题
  • 微信怎么开小店?【企业商家微信开店】
  • Java 中FastJson的使用【吃透FastJson】
  • Redis5.0集群搭建
  • 继企业级信息系统开发学习1.1 —— Spring配置文件管理Bean
  • Web 容器、HTTP 服务器 、Servlet 容器区别与联系