从中序与后序遍历序列构造二叉树
解法
使用后序遍历和中序遍历构造二叉树的方式如图所示:
- 根据后序遍历的特点,最后一个节点就是这个树(或者子树)的根节点,而中序遍历的特点是左子树的节点和右子树的节点分布在这个树(或者子树)的根节点的两边。
- 所以我们可以先通过后序遍历得到树的根节点(最后一个值),然后从中序遍历中找到这个值,这样左子树和右子树的节点个数就能得到了(分布在中序遍历根节点的两边)。
- 得到了左子树(M个节点)和右子树(N个节点)的节点个数,就可以根据这个数目从后序遍历也找到左子树(后序遍历的前M个节点)和右子树(接在后序遍历前M个节点后的N个节点)。
- 对于从中序中得到的左子树和后序中得到的左子树以及从中序中得到的右子树和后序中得到的右子树,我们可以通过重复上面的1,2,3,三步操作来进行。
- 函数返回值为根节点。
代码如下:
def buildTree(inorder, postorder):
# postL、postR、inL、inR分别是后序遍历的左边界、右边界;中序遍历的左边界、右边界
def build(postL,postR,inL,inR):
if postL>postR: # 如果后序遍历中没有节点了,就返回空节点
return None
# 通过后序遍历得到根节点
node = TreeNode(postorder[postR],None,None)
# 遍历中序遍历,从中找到根节点的索引位置
k = 0
for i in range(inL,inR+1):
if inorder[i] == postorder[postR]:
k = i
break
# 得到左子树的节点个数(有了这个就能计算出左右子树在中序遍历、后序遍历的坐标范围了)
numleft = k-inL # 左子树的节点个数
# 根节点的左孩子是左子树的根节点,递归重复即可
# [postL,postL+numleft-1]是左子树在后序遍历的坐标范围
# [inL,k-1]是左子树在中序遍历的左边范围
node.left = build(postL,postL+numleft-1,inL,k-1)
# 根节点的右孩子是右子树的根节点,递归重复即可
# 同上
node.right = build(postL+numleft,postR-1,k+1,inR)
return node # 返回根节点
return build(0,len(postorder)-1,0,len(inorder)-1) # 返回根节点
从前序与中序遍历序列构造二叉树
解法
原理同上:只不过这回要根据前序遍历的性质来改写代码,根据前序遍历的性质,根节点是第一个值,然后左子树的节点,接着右子树的节点。
代码如下:
def buildTree(preorder, inorder):
def build(preL,preR,inL,inR):
if preL>preR:
return None
node = TreeNode(preorder[preL],None,None)
k = 0
for i in range(inL,inR+1):
if inorder[i]==preorder[preL]:
k = i
break
numleft = k - inL
node.left = build(preL+1,preL+numleft,inL,k-1)
node.right = build(preL+numleft+1,preR,k+1,inR)
return node
return build(0,len(preorder)-1,0,len(inorder)-1)
总结
算法
构造步骤:
- 函数需要四个参数 ( i n L , i n R , p r e L ( p o s t L ) , p r e R ( p o s t R ) ) ( inL, inR, preL(postL), preR(postR) ) (inL,inR,preL(postL),preR(postR))
- 首先给出函数递归退出条件:那就是前(后)序遍历中没有节点了,即preL(postL)>preR(postR)
- 然后根据前(后)序遍历的性质得到根节点
- 在中序遍历中找到根节点的索引
- 通过索引计算左子树的节点个数
- 递归得到根节点的左孩子和右孩子
- 返回根节点