根据前序,中序或者后序,中序重构树。这里仅以后序+中序进行分析。文末给出前序的类似代码,原理一致。
回到重构二叉树的问题,这其实是个常见的简单问题,重点就是把坐标细节搞定。
# version 1
# 递归构造
# 时间,每层时间和N,深度最多N,一般情况是logN,所以最差N^2,一般情况NlogN
# 额外空间,无
# Runtime: 356 ms, faster than 10.68% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 18.7 MB, less than 60.97% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
def rebuild(pl, ph, il, ih):
if il >= ih: return None
root = TreeNode(val = postorder[ph-1])
# search in indorder
for idx in range(il, ih):
if inorder[idx] == root.val: break
mid = ph-1-(ih-1-idx)
root.right = rebuild(mid, ph-1, idx+1, ih)
root.left = rebuild(pl, mid, il, idx)
return root
return rebuild(0, len(inorder), 0, len(inorder))
利用map来保证 O ( N ) O(N) O(N)的时间效率。你永远都可以考虑优先用空间换时间。
# version 2
# 空间换时间
# Runtime: 52 ms, faster than 66.41% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 19.4 MB, less than 53.98% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
num2idx = dict(zip(inorder, range(len(inorder))))
def rebuild(pl, ph, il, ih):
if il >= ih: return None
root = TreeNode(val = postorder[ph-1])
# search in indorder
idx = num2idx[root.val]
mid = ph-1-(ih-1-idx)
root.right = rebuild(mid, ph-1, idx+1, ih)
root.left = rebuild(pl, mid, il, idx)
return root
return rebuild(0, len(inorder), 0, len(inorder))
一种严格的 O ( N ) O(N) O(N)算法。非常优雅的实现。
理解该算法并不容易,要点就是要记住递归函数的定义,然后相信它。它不再是单独重构某一边的子树,而是定义为:给定stop,高速你在inoreder找到什么停止,函数负责把我的整颗树从数组里重构出来。
所有节点pop一次,所以复杂度 O ( N ) O(N) O(N).
话又说回来,递归算法,定义是最重要的,思考的关键是相信定义。可是,这比较适合用来理解递归代码。设计还是很难,这道题的实现非常优雅。
# version 3 O(n)的算法
# Runtime: 32 ms, faster than 99.61% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 17.7 MB, less than 94.95% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
# 告知stop,从二个数组重构整颗树,初始的stop为None
def rebuild(stop):
if inorder[-1] != stop:
# 新建当前节点
root = TreeNode(val=postorder.pop())
# 重构右子树,inoreder的stop为当前节点的值,找到则右子树重构完毕
root.right = rebuild(root.val)
# 注意右子树重构完毕,意味着inorder中已经不存在左子树的节点了
# 那么现在该干什么?当然是pop掉当前的节点了
inorder.pop()
# 重构左子树,左子树的stop值为本函数的stop相同,初始为None
root.left = rebuild(stop)
return root
# 哨兵节点
inorder = [None] + inorder
return rebuild(None)
# version 1
# 递归构造
# Runtime: 356 ms, faster than 10.68% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 18.7 MB, less than 60.97% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
def rebuild(pl, ph, il, ih):
if il >= ih: return None
root = TreeNode(val = postorder[ph-1])
# search in indorder
for idx in range(il, ih):
if inorder[idx] == root.val: break
mid = ph-1-(ih-1-idx)
root.right = rebuild(mid, ph-1, idx+1, ih)
root.left = rebuild(pl, mid, il, idx)
return root
return rebuild(0, len(inorder), 0, len(inorder))
# version 2
# 空间换时间
# Runtime: 52 ms, faster than 66.41% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 19.4 MB, less than 53.98% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
num2idx = dict(zip(inorder, range(len(inorder))))
def rebuild(pl, ph, il, ih):
if il >= ih: return None
root = TreeNode(val = postorder[ph-1])
# search in indorder
idx = num2idx[root.val]
mid = ph-1-(ih-1-idx)
root.right = rebuild(mid, ph-1, idx+1, ih)
root.left = rebuild(pl, mid, il, idx)
return root
return rebuild(0, len(inorder), 0, len(inorder))
# version 3 O(n)的算法
# Runtime: 32 ms, faster than 99.61% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
# Memory Usage: 17.7 MB, less than 94.95% of Python online submissions for Construct Binary Tree from Inorder and Postorder Traversal.
class Solution(object):
def buildTree(self, inorder, postorder):
# 告知stop,从二个数组重构整颗树,初始的stop为None
def rebuild(stop):
if inorder[-1] != stop:
# 新建当前节点
root = TreeNode(val=postorder.pop())
# 重构右子树,inoreder的stop为当前节点的值,找到则右子树重构完毕
root.right = rebuild(root.val)
# 注意右子树重构完毕,意味着inorder中已经不存在左子树的节点了
# 那么现在该干什么?当然是pop掉当前的节点了
inorder.pop()
# 重构左子树,左子树的stop值为本函数的stop相同,初始为None
root.left = rebuild(stop)
return root
# 哨兵节点
inorder = [None] + inorder
return rebuild(None)