数据结构奇妙旅程之二叉树题型解法总结

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱
ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客
本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶
个人主页:xiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客
系列专栏:xiaoxie的JAVA系列专栏——CSDN博客●'ᴗ'σσணღ*
我的目标:"团团等我💪( ◡̀_◡́ ҂)" 

( ⸝⸝⸝›ᴥ‹⸝⸝⸝ )欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​+关注(互三必回)!

 一.关于二叉树的遍历的总结

1.使用递归来遍历二叉树

使用递归的方法来遍历二叉树我相信大家应该都没有什么大问题,在这里就不过多的赘述了,直接上代码

1.前序遍历(按照根 -> 左 -> 右)

public void preOrder(TreeNode root) {if(root == null) {return;}System.out.print(root.val + " ");preOrder(root.left);preOrder(root.right);}

2.中序遍历(按照左 -> 根 -> 右)

public void inOrder(TreeNode root) {if(root == null){return;}inOrder(root.left);System.out.print(root.val + " ");inOrder(root.right);}

3.后序遍历(按照左 -> 右 -> 根)

public void postOrder(TreeNode root) {if(root == null){return;}postOrder(root.left);postOrder(root.right);System.out.print(root.val + " ");}

2.迭代解法 (借助栈的思想)

迭代解法本质上是在模拟递归,因为在递归的过程中使用了系统栈,所以在迭代的解法中常用 Stack 来模拟系统栈。

1.前序遍历(按照根 -> 左 -> 右)

首先我们应该创建一个 Stack 用来存放节点,首先我们想要打印根节点的数据,此时 Stack 里面的内容为空,所以我们优先将头结点加入 Stack,然后打印。

之后我们应该先打印左子树,然后右子树。所以先加入 Stack 的就是右子树,然后左子树。
此时你能得到的流程如下:

 代码为:

class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();//打印的容器if(root == null) return list;Stack<TreeNode> stack = new Stack<>(); //创建一个栈TreeNode cur = root;//用cur来遍历二叉树while (!stack.isEmpty() || cur != null) {while (cur != null) {list.add(cur.val);stack.push(cur);cur = cur.left;}TreeNode top = stack.pop();cur = top.right;}return list;}
}

OJ练习为144. 二叉树的前序遍历 - 力扣(LeetCode)

2.中序遍历 按照(左 ->根 -> 右)

尽可能的将这个节点的左子树压入 Stack,此时栈顶的元素是最左侧的元素,其目的是找到一个最小单位的子树(也就是最左侧的一个节点),并且在寻找的过程中记录了来源,才能返回上层,同时在返回上层的时候已经处理完毕左子树了。当处理完最小单位的子树时,返回到上层处理了中间节点。(如果把整个左中右的遍历都理解成子树的话,就是处理完 左子树->中间(就是一个节点)->右子树)同理创建一个 Stack,然后按 左 中 右的顺序输出节点,只是输出的顺序改变了这里直接上代码

class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();TreeNode cur = root;Stack<TreeNode> stack = new Stack<>();while(cur != null || !stack.isEmpty()){while(cur != null) {stack.push(cur);cur = cur.left;}TreeNode top = stack.pop();list.add(top.val);cur = top.right;}return list;}
}

OJ练习为94. 二叉树的中序遍历 - 力扣(LeetCode)

3. 中序遍历 按照(左 ->右 -> 根 )

方法1

  1. 前序遍历的过程 是 根左右。
  2. 将其转化成 根右左。也就是压栈的过程中优先压入左子树,在压入右子树。
  3. 然后将这个结果返回来,这里是利用栈的先进后出倒序打印
class Solution {public List<Integer> postorderTraversal(TreeNode root) {Deque<TreeNode> stack = new LinkedList<>();LinkedList<Integer> ans = new LinkedList<>();if (null == root) return ans;stack.addFirst(root);while(!stack.isEmpty()) {TreeNode node = stack.removeFirst();ans.addFirst(node.val);if (null != node.left) {stack.addFirst(node.left);}if (null != node.right) {stack.addFirst(node.right);}}return ans;}
}

 方法2.

栈遍历版本: 建议先做中序遍历,后序只是在中序上多了一些操作。

与中序的不同之处在于:

  • 中序遍历中,从栈中弹出的节点,其左子树是访问完了,可以直接访问该节点,然后接下来访问右子树。
  • 后序遍历中,从栈中弹出的节点,我们只能确定其左子树肯定访问完了,但是无法确定右子树是否访问过。

因此,我们在后序遍历中,引入了一个prev来记录历史访问记录。

  • 当访问完一棵子树的时候,我们用prev指向该节点。
  • 这样,在回溯到父节点的时候,我们可以依据prev是指向左子节点,还是右子节点,来判断父节点的访问情况。
class Solution{public List<Integer> method1(TreeNode root) {List<Integer> ans=new LinkedList<>();Stack<TreeNode> stack=new Stack<>();TreeNode prev=null;//主要思想://由于在某颗子树访问完成以后,接着就要回溯到其父节点去//因此可以用prev来记录访问历史,在回溯到父节点时,可以由此来判断,上一个访问的节点是否为右子树while(root!=null||!stack.isEmpty()){while(root!=null){stack.push(root);root=root.left;}//从栈中弹出的元素,左子树一定是访问完了的root=stack.pop();//现在需要确定的是是否有右子树,或者右子树是否访问过//如果没有右子树,或者右子树访问完了,也就是上一个访问的节点是右子节点时//说明可以访问当前节点if(root.right==null||prev==root.right){ans.add(root.val);//更新历史访问记录,这样回溯的时候父节点可以由此判断右子树是否访问完成prev=root;root=null;}else{//如果右子树没有被访问,那么将当前节点压栈,访问右子树stack.push(root);root=root.right;}}return ans;}
}

  OJ练习为145. 二叉树的后序遍历 - 力扣(LeetCode)

二.关于二叉树子树的问题

1.100. 相同的树 - 力扣(LeetCode)

要想知道两棵树是否相同,首先我们需要比较结构是否相同,然后再比较值是否相同,同样也可以分为递归和迭代两种方法

1.递归方法

class Solution {public boolean isSameTree(TreeNode p, TreeNode q) {if(p == null && q == null) return true;if(p == null && q != null || p != null && q == null) return false;if(p.val != q.val) return false;return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);}

2.迭代的方法

class Solution {public boolean isSameTree(TreeNode p, TreeNode q) {if(p == null && q == null) return true;if (p == null || q == null) return false;Queue<TreeNode> queue = new LinkedList<TreeNode>();queue.offer(p);queue.offer(q);while(!queue.isEmpty()) {p = queue.poll();q = queue.poll();if(p == null && q == null) continue;if((p == null || q == null) || p.val != q.val)return false;  queue.offer(p.left);queue.offer(q.left);queue.offer(p.right);queue.offer(q.right);     }return true;}
}

 通过借助队列来判断两棵树的结构和节点的值是否相同,需要注意的是两种方法的时间复杂度都为O(n)。

2.572. 另一棵树的子树 - 力扣(LeetCode)

​主要思路:将是否为子树的问题转换成是否相等的问题

class Solution {public boolean isSubtree(TreeNode root, TreeNode subRoot) {if(root == null) return false;if(isSameTree(root,subRoot)) return true;//判断中是否相同if(isSubtree(root.left,subRoot.left)) return true;//判断左子树是否相同if(isSubtree(root.right,subRoot.right)) return true;//判断右子树是否相同return false;}private boolean isSameTree(TreeNode root, TreeNode subRoot) {if(root == null && subRoot == null) return true;if(root == null && subRoot != null ||root != null && subRoot == null) return false;if(root.val != subRoot.val) return false;return isSameTree(root.left,subRoot.left) && isSameTree(root.right,subRoot.right);}
}

其中需要注意的是

判断两个树是否相等的三个条件是的关系,

即当前两个树的根节点值相等
并且,s的左子树和 r 的左子树相等;
并且,s 的右子树和 r 的右子树相等 

判断s 是否为 r的子树的三个条件是的关系

即当前两棵树相等
或者,s 是 r 的左子树
或者,s 是 r 的右子树

总的来说以上这些方法都是比较简单的暴力或者是递归解法,可能还达不到面试的高度,但学习就是循序渐进的,学会了这些比较基础的解法我相信你在后面学习更优的解法时,肯定更加得心应手

三.二叉树的广度优先搜索的总结(BFS)

1.层序遍历

二叉树的层序遍历问题借助队列可以使用广度优先搜索(BFS)算法来实现这种方法可以保证按层遍历二叉树,先遍历完当前层的节点,再遍历下一层的节点,直到所有节点都被遍历完成 

1.基础的二叉树的层序遍历

                                                                                                                                                            题目要求的二叉树的 从上至下 打印(即按层打印),又称为二叉树的 广度优先搜索(BFS)。BFS 通常借助 队列 的先入先出特性来实现。接下来我将一步步的详细讲解

我们先将根节点放到队列中,然后不断遍历队列。

再取出根节点,如果左子树或者右子树不为空,就将他们放入队列中。第一遍处理完后,根节点已经从队列中拿走了,而根节点的两个孩子已放入队列中了,现在队列中就有两个节点即为B和C

同理再取出B和C,如果B的左右孩子不为空,就加入队列,C同理,这样就完成了层层遍历了

 根据上图的解释我们就可以得到如下的代码

class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> res = new ArrayList<>();if(root == null) return res;Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);//首先先把根节点加入到队列中去while(!queue.isEmpty()) {List<Integer> list = new ArrayList<>();int size = queue.size();//根据队列的长度来判断是否为同一层的节点while(size >0) {TreeNode cur = queue.poll();list.add(cur.val);size--;if(cur.left != null) {//左孩子不为空,就加入到队列中queue.offer(cur.left);}if(cur.right != null) {//右孩子不为空,就加入到队列中queue.offer(cur.right);} }res.add(list);}return res;}
}

   

复杂度分析

记树上所有节点的个数为 n。

时间复杂度:每个结点进队出队各一次,故时间复杂度为 O(n)。
空间复杂度:队列中元素的个数不超过 n 个,故空间复杂度为 O(n)。

OJ链接为:102. 二叉树的层序遍历 - 力扣(LeetCode)

2.层序遍历的应用

这题就看成和上一题一样使用广度优先搜索(BFS),借助队列只不过是这题需要稍微改变一些,输出的是每一层的最后一个。所以可以得到以下的代码

class Solution {public List<Integer> rightSideView(TreeNode root) {List<Integer> list = new ArrayList<>();if(root == null) return list;Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while(!queue.isEmpty()) {int size = queue.size();//根据队列的长度判断是每一层有多少个节点while(size > 0) {TreeNode cur = queue.poll();if(size == 1) {//如果是每一层的最后一个节点就输出它list.add(cur.val);}size--;if(cur.left != null) {queue.offer(cur.left);}if(cur.right != null) {queue.offer(cur.right);}}}return list;}
}

复杂度分析

时间复杂度 : O(n)。 每个节点最多进队列一次,出队列一次,因此广度优先搜索的复杂度为线性。

空间复杂度 : O(n)。每个节点最多进队列一次,所以队列长度最大不不超过n,所以这里的空间代价为 O(n)。

 当然这题也可以用深度优先搜索(DFS),根据题意我们每次都要输出每一层最右边的那一个,然后根据 根 -> 右 -> 左 的顺序这样的话每次第一个搜索的就是最右边的那个节点即每一层的最后一个节点 所以我们可以得到以下代码

class Solution {List<Integer> list = new ArrayList<>();public List<Integer> rightSideView(TreeNode root) {dfs(root,0);return list;}private void dfs(TreeNode root,int depth) {if(root == null) return;//先访问 当前节点,再递归地访问 右子树 和 左子树。if(depth == list.size()) {//如果当前节点所在深度还没有出现在res里,说明在该深度下当前节点是第一个被访问的节点,因此将当前节点加入res中。list.add(root.val);}depth++;dfs(root.right,depth);dfs(root.left,depth);} 
}

复杂度分析

时间复杂度 : O(n)

空间复杂度 : O(n)

当然这一题无论是DFS 还是 BFS 都可以解这题,时间复杂度和空间复杂度都差不多,只不过DFS更快一点,BFS更容易理解。

OJ链接为:199. 二叉树的右视图 - 力扣(LeetCode)                                                                                                                                                                                                                                  以上就是博主最近学习二叉树总结的所有内容呢,可能总结的并不是那么全面,不过后续博主学习的更加清楚明白一点,会再将这个总结补全完整,感谢大家的观看。                                         

                                                                                                                                                                                                                        

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

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

相关文章

【深度学习】CodeFormer训练过程,如何训练人脸修复模型CodeFormer

文章目录 BasicSR介绍环境数据阶段 I - VQGAN阶段 II - CodeFormer (w0)阶段 III - CodeFormer (w1) 代码地址&#xff1a;https://github.com/sczhou/CodeFormer/releases/tag/v0.1.0 论文的一些简略介绍&#xff1a; https://qq742971636.blog.csdn.net/article/details/134…

链路追踪-调用链跟踪-Jaeger

文章目录 一、什么是链路跟踪二、OpenCensusOpenCensus 主要特点OpenTracing标准基本概念Span 三、典型服务端产品什么是OpenTracing?opentracing 使用介绍 四、JaegerJaeger 包含的模块Jaeger-client&#xff08;客户端库&#xff09; 五、Jaeger服务容器化部署过程问题整理 …

csdn黑色背景用法

在edge浏览器下&#xff0c;下载油猴脚本管理器 脚本下载 edge扩展 效果图如下&#xff1a;&#xff1a;&#xff1a;

[ACM学习] 进制转换

进制的本质 本质是每一位的数位上的数字乘上这一位的权重 将任意进制转换为十进制 原来还很疑惑为什么从高位开始&#xff0c;原来从高位开始的&#xff0c;可以被滚动地乘很多遍。 将十进制转换为任意进制

适合深夜发朋友圈的心灵鸡汤(整理70句)

1、很多时候&#xff0c;我们赢得了口舌&#xff0c;却失去了感情。 2、失恋到极致的时候&#xff0c;我真的会用后退来保护自己。 3、全身心地去爱&#xff0c;你可能会受到伤害&#xff0c;但这是完整人生的唯一方式。 4、自由不是想干什么就干什么&#xff0c;而是不想干…

Linux中LVM实验

LVM实验&#xff1a; 1、分区 -L是大小的意思-n名称的意思 从vg0&#xff08;卷组&#xff09;分出来 2、格式化LV逻辑卷 LVM扩容 如果icdir空间不够了&#xff0c; 扩展空间lvextend -L 5G /dev/vg0/lv1 /dev/vg0/lv1(pp,vg,lv) 刷新文件系统xfs_growfs /lvdir VG扩容 …

php:规范小数位数,例:10.00展示为10,10.98展示为10.98

代码 <?php$value 10.98; // 原始的双精度类型值if ($value floor($value)) {$formattedValue number_format($value, 0); // 10.00 转换为 10echo $formattedValue;} else {$formattedValue number_format($value, 2); // 10.98 保持为 10.98echo $formattedValue;} …

Sublime Text 3配置 Java 开发环境

《开发工具系列》 《开发语言-Java》 Sublime Text 3配置 Java 开发环境 一、引言二、主要内容1. 初识 Sublime Text 32. 初识 Java3. 接入 Java3.1 JDK 下载3.2 安装和使用 java3.3 环境变量配置 4. 配置 Java 开发环境5. 编写 Java 代码6. 编译和运行 Java 代码7. 乱码问题 三…

服务器无法访问外网怎么办

目前是互联网时代&#xff0c;网络已经成为人们日常生活中不可或缺的一部分。我们通过网络获取信息、进行沟通、甚至进行工作&#xff0c;因此&#xff0c;保持网络的稳定和通畅是非常重要的。然而&#xff0c;有时候我们可能会遇到一些网络无法访问外网的问题&#xff0c;这给…

作者推荐 | 【深入浅出MySQL】「底层原理」探秘缓冲池的核心奥秘,揭示终极洞察

探秘缓冲池的核心奥秘&#xff0c;揭示终极洞察 缓存池BufferPool机制MySQL缓冲池缓冲池缓冲池的问题 缓冲池的原理数据预读程序的局部性原则&#xff08;集中读写原理&#xff09;时间局部性空间局部性 innodb的数据页查询InnoDB的数据页InnoDB缓冲池缓存数据页InnoDB缓存数据…

[DIOR | DIOR-R]旋转目标检测数据集——基于YOLOv8obb,map50已达81.8%

DIOR是一个用于光学遥感图像目标检测的大规模基准数据集。涵盖20个对象类。这20个对象类是飞机、机场、棒球场、篮球场、桥梁、烟囱、水坝、高速公路服务区、高速公路收费站、港口、高尔夫球场、地面田径场、天桥、船舶、体育场、储罐、网球场、火车站、车辆和风磨。 1. DIOR简…

常见の算法链表问题

时间复杂度 1.链表逆序 package class04;import java.util.ArrayList; import java.util.List;public class Code01_ReverseList {public static class Node {public int value;public Node next;public Node(int data) {value data;}}public static class DoubleNode {publi…

Java 字符串 05 练习-遍历字符串和统计字符个数

代码&#xff1a; import java.util.Scanner; public class practice{public static void main(String[] args) {//键盘录入一个字符串&#xff0c;并进行遍历&#xff1b;Scanner input new Scanner(System.in);System.out.println("输入一个字符串&#xff1a;")…

webassembly003 whisper.cpp的main项目-1

参数设置 /home/pdd/le/whisper.cpp-1.5.0/cmake-build-debug/bin/main options:-h, --help [default] show this help message and exit-t N, --threads N [4 ] number of threads to use during computation-p N, --processors …

Android App开发-简单控件(2)——视图基础

2.2 视图基础 本节介绍视图的几种基本概念及其用法&#xff0c;包括如何设置视图的宽度和高度&#xff0c;如何设置视图的外部间距和内部间距&#xff0c;如何设置视图的外部对齐方式和内部对齐方式等等。 2.2.1 设置视图的宽高 手机屏幕是块长方形区域&#xff0c;较短的那…

【星海随笔】unix 启动问题记录.

启动Ubuntu操作系统时&#xff0c;直接进入GRUB状态。 调试时候&#xff0c;曾显示 no bootable device no known filesystem detected 注意&#xff1a; 目前 GRUB 分成 GRUB legacy 和 GRUB 2。版本号是 0.9x 以及之前的版本都称为 GRUB Legacy &#xff0c;从 1.x 开始的就称…

NODE笔记 2 使用node操作飞书多维表格

前面简单介绍了node与简单的应用&#xff0c;本文通过结合飞书官方文档 使用node对飞书多维表格进行简单的操作&#xff08;获取token 查询多维表格recordid&#xff0c;删除多行数据&#xff0c;新增数据&#xff09; 文章目录 前言 前两篇文章对node做了简单的介绍&#xff…

eNSP学习——配置通过STelnet登陆系统

目录 背景 实验内容 实验目的 实验步骤 实验拓扑 详细配置过程 基础配置 配置SSH server 配置SSH client 配置SFTP server 与client 背景 由于Telnet缺少安全的认证方式&#xff0c;而且传输过程采用的是TCP进行明文传输。单纯的提供Telnet服务容易招致主机IP地址欺骗、路…

数据分析 - 图形化解释(后续添加)

图形化解释 作为数据分析师来说一个好的图形&#xff0c;就是自己的数据表达能力 简单文本 只有一两项数据需要分享的时候&#xff0c;简单的文本是最佳的沟通方法 下图的对比可以看出来文字的表达效果会好很多 散点图 散点图在展示两件事的关系时很有用&#xff0c;观察是否存…

【搞懂设计模式】命令模式:从遥控器到编程的妙用!

我们都熟悉电视遥控器&#xff0c;它有许多按钮&#xff0c;每个按钮都有确定的功能。你按下电源键电视就会打开&#xff0c;再按下一次电视就会关闭。编程世界里也有这种模式&#xff0c;这就是我们说的命令模式。 命令模式是一种设计模式&#xff0c;它把一个请求或操作封装…