105. 从前序与中序遍历序列构造二叉树;106. 从中序与后序遍历序列构造二叉树
105. 从前序与中序遍历序列构造二叉树
2020/2/17:
C++:
1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) 13 { 14 TreeNode *root=func(preorder,inorder,0,preorder.size()-1,0,inorder.size()-1); 15 return root; 16 } 17 TreeNode *func(vector<int>& preorder, vector<int>& inorder,int le_p,int ri_p,int le_i,int ri_i) 18 { 19 if(ri_p<le_p) 20 { 21 return nullptr; 22 } 23 TreeNode *root=new TreeNode(preorder[le_p]); 24 auto iter=find(inorder.begin(),inorder.end(),preorder[le_p]); 25 int distance=iter-1-inorder.begin()-le_i; 26 root->left=func(preorder,inorder,le_p+1,le_p+1+distance,le_i,le_i+distance); 27 root->right=func(preorder,inorder,le_p+1+distance+1,ri_p,le_i+distance+2,ri_i); 28 return root; 29 } 30 };
思路蛮简单的:前序序列首元素是根节点,然后在中序序列中找该值,左侧为左子树,右侧为右子树,对两个子树递归调用即可。
一开始写的:
# 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))
进击的小🐴农