题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。(事实上,返回的是二叉树的根节点)
LeetCode
类比LeetCode题目有105. 从前序与中序遍历序列构造二叉树
思路
前序 [1,2,4,7,3,5,6,8]
后序 [4,7,2,15,3,8,6]
前序遍历的第一个是根节点1,扫描中序遍历结果,就可以找到根节点1的位置。在中序遍历中,根节点1左边的数字位于左子树,1右边的节点位于右子树。这样就可以划分出根节点和对应的孩子。
同样地,在左子树和右子树当中,前序遍历的第一个节点是根节点。可以用上面同样的方法去构建,这样也就使用递归的方法来构建二叉树。
代码
def buildTree(self, preorder, inorder):
"""
:type preorder: List[int]
:type inorder: List[int]
:rtype: TreeNode
"""
#递归终止条件 任何一种遍历为空
if not preorder or not inorder:
return None
#前序第一个为root
root = TreeNode(preorder[0])
#找到中序遍历中root的位置
index = inorder.index(preorder[0])
# 递归调用 求左子树 右子树
# 左子树为 前序中根节点后一个后面index个数 中序中从开始到根节点
root.left = self.buildTree(preorder[1:index+1], inorder[:index])
# 右子树为 前序中从index+1后面的 中序从根节点后一个开始到最后
root.right = self.buildTree(preorder[index+1:], inorder[index+1:])
return root
扩展1 中序后序建立二叉树
LeetCode地址:
106. 从中序与后序遍历序列构造二叉树
思路分析
使用递归来解决问题
首先确定根节点,后序遍历的最后一个元素是根节点。根据根节点在中序遍历中的位置,可以将中序遍历分割为左子树和右子树。
同样地,左子树的节点个数可以通过中序遍历中的根节点位置来确定。中序遍历中从最开始到根节点的长度就是左子树的个数。
code
def buildTree(self, inorder: 'List[int]', postorder: 'List[int]') -> 'TreeNode':
if not inorder or not postorder:
return None
root = TreeNode(postorder[-1]) # 后序遍历的最后一个节点是根节点
index = inorder.index(postorder[-1]) # 找到中序遍历中的根节点位置
root.left = self.buildTree(inorder[:index],postorder[:index])
root.right = self.buildTree(inorder[index+1:],postorder[index:-1])
return root
扩展2 前序后序建立二叉树
需要注意的是:前序和后序并不能够唯一确定一颗二叉树。
LeetCode地址:
889. 根据前序和后序遍历构造二叉树
算法思想
思路
前序遍历为:
(root node) (preorder of left branch) (preorder of right branch)
而后序遍历为:
(postorder of left branch) (postorder of right branch) (root node)
例如,如果最终的二叉树可以被序列化的表述为 [1, 2, 3, 4, 5, 6, 7],那么其前序遍历为 [1] + [2, 4, 5] + [3, 6, 7],而后序遍历为 [4, 5, 2] + [6, 7, 3] + [1].
如果我们知道左分支有多少个结点,我们就可以对这些数组进行分组,并用递归生成树的每个分支。
算法
我们令左分支有 L 个节点。我们知道左分支的头节点为 pre[1],但它也出现在左分支的后序表示的最后。所以 pre[1] = post[L-1](因为结点的值具有唯一性),因此 L = post.indexOf(pre[1]) + 1。
现在在我们的递归步骤中,左分支由 pre[1 : L+1] 和 post[0 : L] 重新分支,而右分支将由 pre[L+1 : N] 和 post[L : N-1] 重新分支。
Code
def constructFromPrePost(self, pre, post):
if not pre: return None
root = TreeNode(pre[0])
if len(pre) == 1: return root
L = post.index(pre[1]) + 1
root.left = self.constructFromPrePost(pre[1:L+1], post[:L])
root.right = self.constructFromPrePost(pre[L+1:], post[L:-1])
return root
复杂度分析
-
时间复杂度:O(N^2),其中 NN 是结点的数目。
-
空间复杂度:O(N),答案所用去的空间。
方法二:递归(节约空间的变体)
我们这里提出一个方法一的变体,使用索引指代子数组 pre 和 post,而不是通过那些子数组的副本。这里,(i0, i1, N) 指的是 pre[i0:i0+N], post[i1:i1+N].
def constructFromPrePost(self, pre, post):
def make(i0, i1, N):
if N == 0: return None
root = TreeNode(pre[i0])
if N == 1: return root
for L in xrange(N):
if post[i1 + L - 1] == pre[i0 + 1]:
break
root.left = make(i0 + 1, i1, L)
root.right = make(i0 + L + 1, i1 + L, N - 1 - L)
return root
return make(0, 0, len(pre))
复杂度分析
-
时间复杂度:O(N^2),其中 N是结点的数目。
-
空间复杂度:O(N),答案所用去的空间。