码迷,mamicode.com
首页 > 其他好文 > 详细

【力扣】从中序与后序遍历序列构造二叉树

时间:2021-01-05 11:32:09      阅读:0      评论:0      收藏:0      [点我收藏+]

标签: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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!