构造二叉树是一个常见的二叉树考点,相比于直接考察二叉树的遍历,这种题目的难度会更大。截止到目前(2020-02-08) LeetCode 关于构造二叉树一共有三道题目,分别是:node
今天就让咱们用一个套路一举攻破他们。python
<!-- more -->git
根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你能够假设树中没有重复的元素。 例如,给出 前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7] 返回以下的二叉树: 3 / \ 9 20 / \ 15 7
咱们以题目给出的测试用例来说解:github
前序遍历是根左右
,所以 preorder 第一个元素必定整个树的根。因为题目说明了没有重复元素,所以咱们能够经过 val 去 inorder 找到根在 inorder 中的索引 i。
而因为中序遍历是左根右
,咱们容易找到 i 左边的都是左子树,i 右边都是右子树。数组
我使用红色表示根,蓝色表示左子树,绿色表示右子树。post
根据此时的信息,咱们能构造的树是这样的:测试
咱们 preorder 继续向后移动一位,这个时候咱们获得了第二个根节点”9“,实际上就是左子树的根节点。ui
咱们 preorder 继续向后移动一位,这个时候咱们获得了第二个根节点”20“,实际上就是右子树的根节点。其中右子树因为个数大于 1,咱们没法肯定,咱们继续执行上述逻辑。spa
根据此时的信息,咱们能构造的树是这样的:3d
咱们不断执行上述逻辑便可。简单起见,递归的时候每次我都开辟了新的数组,这个实际上是没有必要的,咱们能够经过四个变量来记录 inorder 和 preorder 的起始位置便可。
代码支持:Python3
Python3 Code:
class Solution: def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: # 实际上inorder 和 postorder必定是同时为空的,所以你不管判断哪一个都行 if not preorder: return None root = TreeNode(preorder[0]) i = inorder.index(root.val) root.left = self.buildTree(preorder[1:i + 1], inorder[:i]) root.right = self.buildTree(preorder[i + 1:], inorder[i+1:]) return root
复杂度分析
空间复杂度忽略了开辟数组的内存消耗。
若是你会了上面的题目,那么这个题目对你来讲也不是难事,咱们来看下。
根据一棵树的中序遍历与后序遍历构造二叉树。 注意: 你能够假设树中没有重复的元素。 例如,给出 中序遍历 inorder = [9,3,15,20,7] 后序遍历 postorder = [9,15,7,20,3] 返回以下的二叉树: 3 / \ 9 20 / \ 15 7
咱们以题目给出的测试用例来说解:
后序遍历是左右根
,所以 postorder 最后一个元素必定整个树的根。因为题目说明了没有重复元素,所以咱们能够经过 val 去 inorder 找到根在 inorder 中的索引 i。
而因为中序遍历是左根右
,咱们容易找到 i 左边的都是左子树,i 右边都是右子树。
我使用红色表示根,蓝色表示左子树,绿色表示右子树。
根据此时的信息,咱们能构造的树是这样的:
其中右子树因为个数大于 1,咱们没法肯定,咱们继续执行上述逻辑。咱们 postorder 继续向前移动一位,这个时候咱们获得了第二个根节点”20“,实际上就是右子树的根节点。
根据此时的信息,咱们能构造的树是这样的:
咱们不断执行上述逻辑便可。简单起见,递归的时候每次我都开辟了新的数组,这个实际上是没有必要的,咱们能够经过四个变量来记录 inorder 和 postorder 的起始位置便可。
代码支持:Python3
Python3 Code:
class Solution: def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode: # 实际上inorder 和 postorder必定是同时为空的,所以你不管判断哪一个都行 if not inorder: return None root = TreeNode(postorder[-1]) i = inorder.index(root.val) root.left = self.buildTree(inorder[:i], postorder[:i]) root.right = self.buildTree(inorder[i+1:], postorder[i:-1]) return root
复杂度分析
空间复杂度忽略了开辟数组的内存消耗。
返回与给定的前序和后序遍历匹配的任何二叉树。 pre 和 post 遍历中的值是不一样的正整数。 示例: 输入:pre = [1,2,4,5,3,6,7], post = [4,5,2,6,7,3,1] 输出:[1,2,3,4,5,6,7] 提示: 1 <= pre.length == post.length <= 30 pre[] 和 post[] 都是 1, 2, ..., pre.length 的排列 每一个输入保证至少有一个答案。若是有多个答案,能够返回其中一个。
咱们以题目给出的测试用例来说解:
前序遍历是根左右
,所以 preorder 第一个元素必定整个树的根,preorder 第二个元素(若是存在的话)必定是左子树。因为题目说明了没有重复元素,所以咱们能够经过 val 去 postorder 找到 pre[1]在 postorder 中的索引 i。
而因为后序遍历是左右根
,所以咱们容易得出。 postorder 中的 0 到 i(包含)是左子树,preorder 的 1 到 i+1(包含)也是左子树。
其余部分能够参考上面两题。
代码支持:Python3
Python3 Code:
class Solution: def constructFromPrePost(self, pre: List[int], post: List[int]) -> TreeNode: # 实际上pre 和 post必定是同时为空的,所以你不管判断哪一个都行 if not pre: return None node = TreeNode(pre[0]) if len(pre) == 1: return node i = post.index(pre[1]) node.left = self.constructFromPrePost(pre[1:i + 2], post[:i + 1]) node.right = self.constructFromPrePost(pre[i + 2:], post[i + 1:-1]) return node
复杂度分析
空间复杂度忽略了开辟数组的内存消耗。
若是你仔细对比一下的话,会发现咱们的思路和代码几乎如出一辙。注意到每次递归咱们的两个数组个数都会减去 1,所以咱们递归终止条件不难写出,而且递归问题规模如何缩小也很容易,那就是数组总长度减去 1。
咱们拿最后一个题目来讲:
node.left = self.constructFromPrePost(pre[1:i + 2], post[:i + 1]) node.right = self.constructFromPrePost(pre[i + 2:], post[i + 1:-1])
咱们发现 pre 被拆分为两份,pre[1:i + 2]和 pre[i + 2:]。很明显总数少了 1,那就是 pre 的第一个元素。 也就是说若是你写出一个,其余一个不用思考也能写出来。
而对于 post 也同样,post[:i + 1] 和 post[i + 1:-1],很明显总数少了 1,那就是 post 最后一个元素。
这个解题模板足够简洁,而且逻辑清晰,你们能够用个人模板试试~
更多题解能够访问个人 LeetCode 题解仓库:https://github.com/azl3979858... 。 目前已经 30K star 啦。
你们也能够关注个人公众号《力扣加加》获取更多更新鲜的 LeetCode 题解