例题
描述
给定节点数为 n 的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。
例如输入前序遍历序列{1,2,4,5,3,6,7}和中序遍历序列{4,2,5,1,6,3,7},则重建出如下图所示。
解决思路
因为先序遍历根中序遍历是保存在数组中的,所以我们需要利用数组的性质,而不是上来就利用二叉树的性质,刚开始我是按照二叉树来做的,直到我失败了无数次之后,看到了函数的传入值是两个list,我好像参悟了什么,又好像没参悟透,然后我就看大佬的代码,看了好多次,手动运行了好多次,始终一知半解,好像懂了,又没懂,然后我就去看了评论区,发现大多数人和我一样,都刚开始没看懂,我才知道,我不是一个人在战斗。然后一气之下不做了,直接玩游戏,刷视频,好好的睡了一觉,起来之后,一切豁然开朗,拿到代码顺序读明白了,现在把这题的思路写下来,帮助后来人。
利用二叉树的性质将数组分类
根据先序遍历,将根节点找到,是1
然后在根据中序遍历,找到左子树和右子树
这个时候根据左右子树,再将先序遍历划分为三部分
至此,二叉树已经处理完毕,现在在处理他的位置
利用数组的位置处理
先序遍历找位置
处理一棵二叉树,肯定要先进入根,所以我们先在先序遍历里面找到根以及根的位置
找到根之后,我们肯定要接着找根,先处理左子树,在处理右子树,那么我们这个时候在找到左子树的根
左子树的根找到了,我们现在先找右子树的根,先不去处理左子树,一会一起处理,因为处理他们的办法是相同的
会发现右子树的根的位置恰好是根节点+左子树的长度
中序遍历找位置
根据先序遍历,我们在中序遍历里面找一下位置
我们可以找到中序遍历中根的位置
因为我们在先序遍历里面已经确定了一下左右子树根的位置,这个时候我们只需要在中序遍历里面确定他们的长度即可
从这我们可以知道左子树的长度为3,根节点的位置,右子树的长度为3,那么我们根据数组的性质,是不是可以推导以下推论:
- 左子树长度+根长度 = 数组总长-右子树长度
- 左子树的边界=0 ~ root-1(因为数组是从0开始计数,所以正好是0-2)
- 右子树的边界=root+1 ~ 数组总长-1(因为数组的长度是按照数量来计算,但是数组是从0开始计数)
至此,我们总结一下,整理成一个表格
这个时候我们发现这样看得懂,但是如何系统化呢?
4. 根节点在先序遍历里面找
5. 长度在中序遍历里面找
得出这样两条解决,我们开始解决
从这里我们可以看出,左子树的左边界我们如果设成left,则右子树的根节点就为i-left+root+1:i-left为左子树的长度+根节点在先序遍历中的位置,这个时候仅仅是根节点根左子树的长度,在加1,就是右子树的根节点
这个时候我们完成了递归公式,但是递归有两要素,结束条件和递归公式,现在我们在找结束条件。
刚才我们是很多数据,这个时候我们采用分治的思想,看看只有两个节点的时候是什么情况
可以发现,当left>right的时候,象征这递归结束了,所以我们把left<right作为结束条件
至此,我们把重建二叉树的全流程结束了。
def reConstructBinaryTree(self , pre: List[int], vin: List[int]) -> TreeNode:
# write code here
def cur(root,left,right):
if left > right:
return
# 先在先序遍历拿到根节点
preroot = pre[root]
# 创建根节点,方便下面递归使用
node = TreeNode(pre[root])
# 在找到根节点的位置
i = in_dic[preroot]
# 根据之前得到的公式,开始遍历
node.left = cur(root + 1,left,i-1)
node.right = cur(i- left + root + 1,i+1,right)
return node
in_dic = {}
# 哈希表使用中序遍历,这样好找I值
for x,i in enumerate(vin):
in_dic[i] = x
return cur(0,0,len(vin)-1)
结论
做树做递归,可以先一步一步推导一下,推导了,也就有规律了,然后在运用分治法进行最小分治就可以解决