二叉搜索树中序遍历_树的前序遍历

二叉搜索树中序遍历_树的前序遍历LeetCode刷题总结之二叉树的构建算法-一道题13种解法前言最近开始刷到一些二叉树的构建的算法题,挺有意思的,打算总结一下。这里总结的都是确定二叉树的构造算法题,可能有多个构造结果的算法题就没考虑。从构造目标上来看,这里讨论的算法题可以分为两种:二叉树的构造二叉搜索树

LeetCode刷题总结之二叉树的构建算法-一道题13种解法   前言   最近开始刷到一些二叉树的构建的算法题,挺有意思的,打算总结一下。这里总结的都是确定二叉树的构造算法题,可能有多个构造结果的算法题就没考虑。   从构造目标上来看,这里讨论的算法题可以分为两种:二叉树的构造二叉搜索树(BST)的构造   从构造条件上来看,这里讨论的算法题也可以分为两种:不含重复数值节点的二叉树的构造含重复数值节点的二叉树的构造   1.从前序与中序遍历以及中序和后序遍历构造二叉树   这2个题目分别为:LeetCode.105 从前序与中序遍历序列构造二叉树,中等难度LeetCode.106 从中序与后序遍历序列构造二叉树,中等难度   1.1 解题方法   首先,按之前我们给分类条件给这两种题目一个定性:它们都是一个不含重复节点的二叉树构造算法题。这2个题目的思路和做法都是一样的:首先从先序/后序序列中找到根节点,根据根节点将中序序列分为左子树和右子树。递归地对左子树和右子树进行二叉树的构造。   思路其实是比较好想的,如果你在面试中遇到了这2个题目,那其实考察的编码的基本功了。虽然比较好想,但是一次把代码写出来且保证AC还是有一定难度的。   1.2 复杂度分析   时间复杂度: O(n),由于每次递归我们的inorder和preorder的总数都会减1,因此我们要递归n次。 空间复杂度: O(n),递归n次,系统调用栈的深度为n。   1.3 Show the code   <pre language=”typescript” code_block=”true”>public class Q105BuildTree {public TreeNode buildTree(int[] preorder, int[] inorder) {int len = preorder.length;if (len == 0) return null;   }   <pre language=”typescript” code_block=”true”>public class Q106BuildTree {public TreeNode buildTree(int[] inorder, int[] postorder) {if (inorder.length == 0) return null;return buildTreeTrace(inorder, 0, inorder.length – 1, postorder, 0, postorder.length – 1);}   }   2. 从前序遍历构造BST以及序列化与反序列化BST   这2个题目分别为:LeetCode.449 序列化和反序列化二叉搜索树,中等难度LeetCode.1008 先序遍历构造二叉树,中等难度   同样地,按之前我们给分类条件给这两种题目一个定性:它们都是一个不含重复节点的二叉搜索树构造算法题。其中Q449的题干描述里面并没有给出“不含重复节点”的条件,但是它的测试用例里面都是“不含重复节点”的用例。这里,我们就暂且给它加上“不含重复节点”的条件。   很显然,仅仅是将这2个题目放在一起,我们就发现可以通过Q1008的解法去搞定Q449。于是我们这里先分析Q1008: 从前序遍历构造BST,随后再分析Q449: 序列化与反序列化BST。   2.0 解题思考   问题1:为什么可以从前序遍历还原一个唯一的节点不重复的BST?前序遍历是:根-左子树-右子树,那么对于一个前序遍历,我们便可以到BST的根节点。拿到根节点后,我们便可以找到左子树的所有节点和右子树的所有节点。同样地,可以左子树和右子树的根节点。由于左子树和右子树也是以根-左-右的方式进行遍历的,那么也适用于上述的构造方式。   问题2:可不可以通过后序遍历还原一个唯一的节点不重复的BST?   答案同理是可以的。正因为如此,下面的给出的5种解法,都对应一种思路类似的从后序遍历构造BST的解法。所以,对于Q449: 序列化与反序列化BST,我们可以将其序列化成前序或者后序遍历,再从对应的遍历构造出BST,这样通过前序或者后序遍历反序列化BST这类解法就一共有10种了。至于,从后序遍历构造BST的5种方法,我这里就不贴了,有兴趣的朋友可以自己写一下或者参考下我的githubQ1008_1BSTFromPostorder。   2.1 解题方法1: 先序遍历和中序遍历构造二叉树   2.1.1 解题思路   首先将先序遍历排序得到中序遍历,随后使用分治的方法从先序遍历和中序遍历构造出二叉搜索树,即前面的方法。   2.1.2 复杂度分析   时间复杂度: O(nlogn),排序。 空间复杂度: O(n),需要存储中序遍历结果。   2.1.3 Show the code   参考前面的代码,就不重复贴了。   2.2 解题方法2:二分查找插入点   2.2.1 解题思路   参考二分插入排序,思路大致如下: 考虑前n-1个点都构造好了,对于第n个点,我们根据BST树的性质二分找到对应的插入点,然后插入第n个点。   2.2.2 复杂度分析   时间复杂度:O(nlogn),插入过程耗时T   空间复杂度:O(1)   
二叉搜索树中序遍历_树的前序遍历
二叉搜索树中序遍历_树的前序遍历   2.2.3 Show the code   <pre language=”typescript” code_block=”true”> public TreeNode bstFromPreorder(int[] preorder) {TreeNode root = new TreeNode(preorder[0]);for (int i = 1; i < preorder.length; i++) {int val = preorder[i];TreeNode node = new TreeNode(val);putNode(root, node);}return root;}   2.3 解题方法3:递归   2.3.1 解题思路   第一个素为root节点,其后的节点比root大的属于root的右子树, 比root小的是属于其左子树,递归构造左右子树。遍历找到左右子树的分界位置。   2.3.2 复杂度分析   时间复杂度:O(n^2),考虑最坏情况,所有节点都在左子树,这种情况递归n次,每次内部迭代1+2+…n-1。   空间复杂度:O(n),递归n次,系统调用栈的深度为n。   2.3.3 Show the code   <pre language=”typescript” code_block=”true”> public TreeNode bstFromPreorder2(int[] preorder) {return bstFromPreorder(preorder, 0, preorder.length – 1);}   2.4 解题方法4:(lower, upper) + 递归   这是LeetCode的官方解法,我花了一会才能理解,感觉有点难想啊。   2.4.1 解题思路   将 lower 和 upper 的初始值分别设置为负无穷和正无穷,因为根节点的值可以为任意值。从先序遍历的第一个素 idx = 0 开始构造二叉树,构造使用的函数名为 helper(lower, upper): 如果 idx = n,即先序遍历中的所有素已经被添加到二叉树中,那么此时构造已经完成; 如果当前 idx 对应的先序遍历中的素 val = preorder[idx] 的值不在 [lower, upper] 范围内,则进行回溯; 如果 idx 对应的先序遍历中的素 val = preorder[idx] 的值在 [lower, upper] 范围内,则新建一个节点 root,并对其左孩子递归处理 helper(lower, val),对其右孩子递归处理 helper(val, upper)。   2.4.2 复杂度分析   时间复杂度:O(n),仅扫描前序遍历一次 空间复杂度:O(n),考虑最坏情况,所有节点都在左子树,这种情况递归n次,系统栈深度n   2.4.3 Show the code   <pre language=”typescript” code_block=”true”> int idx = 0;int[] preorder;int n;   2.5 解题方法5:迭代   这也是LeetCode的官方解法,我第一次解题的思路和这个类似,不过当时处理逻辑没想清楚。   2.5.1 解题思路   将先序遍历中的第一个素作为二叉树的根节点,即 root = new TreeNode(preorder[0]),并将其放入栈中。使用 for 循环迭代先序遍历中剩下的所有素:将栈顶的素作为父节点,当前先序遍历中的素作为子节点。如果栈顶的素值小于子节点的素值,则将栈顶的素弹出并作为新的父节点,直到栈空或栈顶的素值大于子节点的素值。注意,这里作为父节点的是最后一个被弹出栈的素,而不是此时栈顶的素;如果父节点的素值小于子节点的素值,则子节点为右孩子,否则为左孩子;将子节点放入栈中。   2.5.2 复杂度分析   时间复杂度:O(n),仅扫描前序遍历一次 空间复杂度:O(n),考虑最坏情况,所有节点都在左子树,队列长度为n   2.5.3 Show the code   <pre language=”typescript” code_block=”true”> public TreeNode bstFromPreorder4(int[] preorder) {int n = preorder.length;if (n == 0) return null;   3. 序列化与反序列化二叉树   这题目为:   LeetCode.297 二叉树的序列化与反序列化,困难难度   同样地,按之前我们给分类条件给这题目一个定性:它是一个含重复节点的二叉树构造算法题。这个题目明显比上述的题目都困难,因为它的条件最宽泛。   3.0 解题思考   问题:下面我们给的第一种解法就是通过带null节点的前序遍历还原二叉树,那么可以通过带null节点中序或者后序遍历来还原吗?这里就我自己的思考,我认为带null节点的前序或者后序遍历是可以还原二叉树的,而中序遍历则不行(可能是我还没写出来解法)。一个比较关键的点就是这2种都可以明晰的知道根节点,这样就能根据根节点递归还原,而中序遍历却无法确定根节点。这里通过后序遍历还原的代码与前序比较类似,我就不贴了,有兴趣的朋友可以自己写一下或者参考下我的githubQ297SerializeAndDeserializeBinaryTree。   3.1 解题方法1:带null节点的前序遍历(DFS)   3.1.1 解题思路   树序列化的时候将叶子节点的左右null节点孩子也保存到先序遍历结果中。反序列化的时候可以根据null节点的信息将还原二叉树。   3.1.2 复杂度分析   序列化: 时间复杂度:O(n),二叉树的前序遍历。 空间复杂度: O(n),递归需要系统栈和非递归需要手动构造的辅助栈。 反序列化: 时间复杂度:O(n),每一个节点处理一次。 空间复杂度: O(n),存储队列。   3.1.3 Show the code   <pre language=”typescript” code_block=”true”> public String serialize(TreeNode root) {StringBuilder res = preOrderNonRecur(root, new StringBuilder());return res.toString();}   3.2 解题方法2:带null节点的层次遍历(BFS)   3.2.1 解题思路   树序列化的时候将叶子节点的左右null节点孩子也保存到层次遍历结果中。反序列化的时候可以根据null节点的信息将还原二叉树。   3.2.2 复杂度分析   序列化: 时间复杂度:O(n),二叉树的层次遍历。 空间复杂度: O(n),辅助队列。 反序列化: 时间复杂度:O(n),每一个节点处理一次。 空间复杂度: O(n),存储队列。   3.2.3 Show the code   <pre language=”typescript” code_block=”true”> /* 层次遍历(BFS)*/public String serialize2(TreeNode root) {if (root == null) {return “”;}   4. 总结   可以发现,序列化和反序列化二叉树作为条件最宽泛的方法是实用于其他条件更强的算法题的。如果也用这个方法去解Q449: 序列化与反序列化BST,我们一共有13种解法,是不是有点夸张~

2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/49799.html

(0)
上一篇 2024年 9月 3日 下午5:36
下一篇 2024年 9月 3日 下午5:42

相关推荐

关注微信