105. 从前序与中序遍历序列构造二叉树
思路蛮简单的:前序序列首元素是根节点,然后在中序序列中找该值,左侧为左子树,右侧为右子树,对两个子树递归调用即可。
一开始写的:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
if not preorder:
return None
root=TreeNode(preorder[0])
index=inorder.index(preorder[0])
root.left=self.buildTree(preorder[1:index+1],inorder[:index])
root.right=self.buildTree(preorder[index+1:],inorder[index+1:])
return root
花了大量时间在list的浅拷贝上,以及index()函数的过多使用。
官方题解的:
是用中序序列先做一个字典,然后每次查找根节点的index时就是O(1)的时间,以及递归函数参数中加上前后界,省去浅拷贝的时间。用了一个全局的变量,每次加一,即每次讲前序序列中第pre_idx 个数作为根,这个我想了半天,后来画了下递归过程,发现每次pre_idx 加一指示的正好是halper函数递归时要选取的根节点。。实在是有点取巧。。
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def buildTree(self, preorder, inorder):
"""
:type preorder: List[int]
:type inorder: List[int]
:rtype: TreeNode
"""
def helper(in_left = 0, in_right = len(inorder)):
nonlocal pre_idx
# if there is no elements to construct subtrees
if in_left == in_right:
return None
# pick up pre_idx element as a root
root_val = preorder[pre_idx]
root = TreeNode(root_val)
# root splits inorder list
# into left and right subtrees
index = idx_map[root_val]
# recursion
pre_idx += 1
# build left subtree
root.left = helper(in_left, index)
# build right subtree
root.right = helper(index + 1, in_right)
return root
# start from first preorder element
pre_idx = 0
# build a hashmap value -> its index
idx_map = {val:idx for idx, val in enumerate(inorder)}
return helper()
时间、空间差距:
106. 从中序与后序遍历序列构造二叉树
思路和上题一样,后序的最后是根节点,还是去中序里去找该节点,再递归左右子树。
1.copy上题的官方题解,不过这里递归过程是从后序序列末尾依次向前选取,那么全局那个变量也要从最后开始依次递减,并且递归时要先递归右子树,再递归左子树。
class Solution:
def buildTree(self, inorder, postorder):
def func(le=0,ri=len(postorder)):
nonlocal x
if ri-le<1:
return None
_val=postorder[x]
x-=1
root=TreeNode(_val)
index=dic.get(_val)
root.right=func(index+1,ri) #半闭半开
#这俩顺序不能反
root.left=func(le,index) #半闭半开
return root
x=len(postorder)-1
dic={value:key for key,value in enumerate(inorder)}
return func()
2.自己写的,四个参数,分别指示两个序列当前考察的两段子序列范围。时间、空间使用情况和上面的写法是基本一致,但好理解一些。
注意这里递归子树时左右的先后就无所谓了,因为当前考察的两段中序、后序子序列范围已经明确给出了,不需要为了什么奇怪的全局变量改动代码布局。。。
class Solution:
def buildTree(self, inorder, postorder):
def func(in_le,in_ri,po_le,po_ri):
if in_ri-in_le<1:
return None
val=postorder[po_ri-1]
root=TreeNode(val)
index=dic.get(val)
root.left=func(in_le,index,po_le,po_le+index-in_le)
root.right=func(index+1,in_ri,po_le+index-in_le,po_ri-1)
return root
dic={value:key for key,value in enumerate(inorder)}
return func(0,len(inorder),0,len(postorder))