-
最近小胖开始找工作了,又来刷苦逼的算法了 555
-
废话不多说,看这一题,上链接:https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/description/?envType=study-plan-v2&envId=top-interview-150
-
看到这一题有两个很重要的基础知识点需要知道
- 什么是二叉树?
- 什么是中序遍历和后序遍历?
-
先回答第一个问题,什么是二叉树?这是一个数据结构,是一种常见的树状数据结构,其中每个节点最多有两个子节点,分别称为左子节点和右子节点,如上图。
-
了解什么是二叉树,才能回答第二问题,什么是中序遍历和后序遍历?首先这两个都是一种遍历整个二叉树的方法,只不过遍历节点的顺序不同产生不同的名字
- 中序遍历:按照左子树 -> 根节点 -> 右子树的顺序遍历二叉树
- 后序遍历:按照左子树 -> 右子树 -> 根节点的顺序遍历二叉树
-
好了,了解了这两个基础知识,现在就来解决这道题。这道题的核心在于你拥有两个顺序的方式,如何构建一棵树呢?
-
从上面例子入手,后序遍历是 9,15,7,20,3,我们知道后序遍历的顺序是 左,右,根,所以左右一个元素肯定是根节点,这棵树的根节点就是 3 。
-
得到了这个信息有什么用呢?用处大了,我们可以拿这个 3 去中序遍历找 左子树的范围 和 右子树的范围,如下图:
-
这样子我们是不是就把左子树和右子树的范围找出来了呢,所以这个过程的逻辑就是这样的:
- 先获取后序遍历数组的最后一个元素,得到根节点
- 再根据根节点 去 中序遍历数组中寻找对应的位置
- 找到位置 i 后,开始圈定范围
- 左子树 元素范围是 0 - i
- 右子树 元素范围是 i+1 - n(数组的最后一个元素)
-
找到范围了,然后咋办呢?
-
这个时候就需要一个很牛逼的思想,分治的思想
-
什么是分治?分治就是一种问题解决策略,它将一个大问题划分为多个相同或类似的子问题,然后递归地解决这些子问题,最后将子问题的解合并起来得到大问题的解。
-
所以这里我们可以将找出的 左子树 当作一个新的树,继续上述的操作,直到只剩一个节点为止,就如上述的 9 节点。右子树同理
-
这样我们的二叉树就可以完成了,具体代码如下:
func dfs106(inorder []int, postorder []int) *TreeNode {
//当中序数组为空,则没有元素,返回nil
if len(inorder) == 0 {
return nil
}
//根节点为后序数组的最后一个元素
head := &TreeNode{Val: postorder[len(postorder)-1]}
//在中序数组中找到根节点的位置
i := 0
for ; i < len(inorder); i++ {
if inorder[i] == postorder[len(postorder)-1] {
break
}
}
//递归构建 左子树
head.Left = dfs106(inorder[:i], postorder[:i])
//递归构建 右子树
head.Right = dfs106(inorder[i+1:], postorder[i:len(postorder)-1])
//返回根节点
return head
}
- 这里可以有个小优化,因为每次递归都需要遍历 中序数组,太过耗时了,本着空间换时间的策略,用一个 map 存储每个中序数组元素的索引,这样查找 左右子树的范围的时候就会快很多,代码如下:
func Solution106v2(inorder []int, postorder []int) *TreeNode {
// 获取中序遍历的索引
n := len(inorder)
index := make(map[int]int, n)
for i, v := range inorder {
index[v] = i
}
var build func(int, int, int, int) *TreeNode
build = func(p1, p2, i1, i2 int) *TreeNode {
// 递归终止条件
if p1 > p2 {
return nil
}
// 根节点
root := &TreeNode{Val: postorder[p2]}
// 中序遍历中根节点的位置
i := index[postorder[p2]]
// 左子树的节点个数
leftSize := i - i1
// 递归构建左右子树
root.Left = build(p1, p1+leftSize-1, i1, i-1)
root.Right = build(p1+leftSize, p2-1, i+1, i2)
return root
}
return build(0, n-1, 0, n-1)
}
- 到这里就结束了,这道题写了我足足一个上午,看来还是太菜了需要多练,不说了接着做题了