标签:ext 过程 int ali bsp 不同 开始 not idt
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal
根据一棵树的中序遍历与后序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
例如,给出
中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
我写的第一版代码如下,大部分测试用例均可以通过:
1 class Solution: 2 def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode: 3 if len(inorder) > 0 and len(postorder) >0 : 4 root = postorder[-1] 5 root_idx = inorder.index(root) 6 result = TreeNode() 7 result.val = inorder[root_idx] 8 result.left = Solution.buildTree(self,inorder[:root_idx],postorder[:root_idx]) 9 result.right = Solution.buildTree(self,inorder[root_idx+1:],postorder[len(inorder[:root_idx]):(len(postorder)-len(inorder[:root_idx]))]) 10 return result
思路如下:
前序遍历:访问顺序:根节点->左节点->右节点
中序遍历:访问顺序:左节点->根节点->右节点
后序遍历:访问顺序:左节点->右节点->根节点
我依稀记得考研复习的时候,给出前中两遍历,可以还原出二叉树;给出中后两遍历。也可以还原出二叉树;给出前后两遍历是不能还原二叉树的。
题目给出了中后两遍历序列,我们首先分析两个list : inorder = [9,3,15,20,7],postorder = [9,15,7,20,3]
因为后序遍历总是最后在访问根节点,所以我们可以知道3为本棵二叉树的根节点,我们又知道中序遍历先访问根节点的左子树(节点)再访问跟节点,于是我们可以在inorder中定位到根节点3,以及这颗二叉树的左子树元素(9)与右子树元素(15,20,7)。
3
/ \
9 (15,20,7)
利用递归的思想,将左子树与右子树看作一个新的二叉树不断重复上述过程,就能得到题目要求的结果。
问题的关键在于每次递归调用,左右子树的inorder,postorder如何准确给出。
我的思路编程是先拿到后序遍历序列的最后一个元素(根节点),在前序遍历序列中找到根节点对应的下标,根节点下标以左的list作为左子树的inorder,根节点下标以右的list作为右子树的inorder;左子树的postorder比较容易获得,因为中后序遍历都是先访问左子树,因此postorder[:root_idx]可以将左子树的postorder获取,而右子树的postorder需要作减法来获得对应的list的取值范围,我选择使用postorder/inorder的总长度减去左子树postorder的长度,获得取值范围,再选择从左子树最后一个元素的下标后的第一个元素作为起始下标,所以这句代码看起来很长:
postorder[len(inorder[:root_idx]):(len(postorder)-len(inorder[:root_idx]))]
但是当len(inorder)与len(postorder)=2时,会有一个小bug执行测试用例inorder = [1,2],postorder = [2,1]出错,手动执行一遍:
root = postorder[-1] = 1
root_idx = inorder.index(root) = 0
建立一棵树 result = TreeNode( )
将根节点的值保存入 result.val = 1
result.left为空
result.right = Solution.buildTree(self,inorder[1:],postorder[0:2]) inorder[1:] = [2] ,postorder = [2,1]
进入buildTree( ),中序遍历与后序遍历长度不同,肯定要报错
是因为我使用减法获取右子树后序遍历序列出现问题当postorder初始值较小时该方法不行,所以
1 class Solution: 2 def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode: 3 if len(inorder) > 0 and len(postorder) > 0 : 4 root = postorder[-1] 5 root_idx = inorder.index(root) 6 result = TreeNode() 7 result.val = inorder[root_idx] 8 result.left = Solution.buildTree(self,inorder[:root_idx],postorder[:root_idx]) 9 result.right = Solution.buildTree(self,inorder[root_idx+1:],postorder[len(inorder[:root_idx]):-1]) 10 return result
转变思路从列表结尾开始思考,发现后序遍历列表从len(inorder[:root_idx])到最后一个元素(根节点)之前均为右子树的后序遍历List,所以直接一个-1,满足所有情况。
当然效果惨不忍睹:
一如既往的看大佬的代码感受差距:
不过思路相同
1 class Solution: 2 def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode: 3 # 实际上inorder 和 postorder一定是同时为空的,因此你无论判断哪个都行 4 if not inorder: 5 return None 6 root = TreeNode(postorder[-1]) 7 i = inorder.index(root.val) 8 root.left = self.buildTree(inorder[:i], postorder[:i]) 9 root.right = self.buildTree(inorder[i+1:], postorder[i:-1]) 10 11 return root 12 13 作者:fe-lucifer 14 链接:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/solution/si-lu-qing-xi-dai-ma-jian-ji-he-105ti-si-lu-yi-zhi/ 15 来源:力扣(LeetCode)
简单起见,递归的时候每次都开辟了新的数组,这个其实是没有必要的,我们可以通过四个变量来记录inorder和postorder的起始位置即可, 具体见下方代码区。
1 class Solution: 2 def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode: 3 def dfs(inorder, i_start, i_end, postorder, p_start, p_end): 4 if i_start > i_end: return None 5 if i_start == i_end: return TreeNode(inorder[i_start]) 6 node = TreeNode(postorder[p_end]) 7 i = inorder.index(postorder[p_end]) 8 node.left = dfs(inorder, i_start, i - 1, postorder, p_start, p_start + (i - 1 - i_start)) 9 node.right = dfs(inorder, i + 1, i_end, postorder, p_start + (i - 1 - i_start) + 1, p_end - 1) 10 return node 11 return dfs(inorder, 0, len(inorder) - 1, postorder, 0, len(postorder) - 1) 12 13 作者:fe-lucifer 14 链接:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/solution/si-lu-qing-xi-dai-ma-jian-ji-he-105ti-si-lu-yi-zhi/ 15 来源:力扣(LeetCode)
updated:(有同学不懂为啥 post_end 是 post_start + i - 1 - in_start,我解释一下)
我上面提到了 实际上 inorder 的 长度和 postorder 长度是一样的。而:
inorder 的长度是 i - 1 - in_start
因此 postorder 的长度也是 i - 1 - in_start
postorder 的长度 = post_end - post_start
因此 post_end 就是 post_start + i - 1 - in_start
复杂度分析
时间复杂度:由于每次递归我们的inorder和postorder的总数都会减1,因此我们要递归N次,故时间复杂度为 O(N),其中N为节点个数。
空间复杂度:我们使用了递归,也就是借助了额外的栈空间来完成, 由于栈的深度为N,因此总的空间复杂度为 O(N),其中N为节点个数。
标签:ext 过程 int ali bsp 不同 开始 not idt
原文地址:https://www.cnblogs.com/Harukaze/p/14222513.html