105. 从前序与中序遍历序列构造二叉树
题目描述
给定一棵树的前序遍历 preorder 与中序遍历 inorder。请构造二叉树并返回其根节点。
示例1
Input: preorder = [3,9,20,15,7], inorder=[9,3,15,20,7]
output:[3,9,20,null,null,15,7]
示例2
Input: preorder=[-1], inorder=[-1]
output: [-1]
提示
- 1 ≤ p r e o r d e r . l e n g t h ≤ 3000 1\le preorder.length \le 3000 1≤preorder.length≤3000
- i n o r d e r . l e n g t h = = p r e o r d e r . l e n g t h inorder.length == preorder.length inorder.length==preorder.length
- − 3000 ≤ p r e o r d e r [ i ] , i n o r d e r [ i ] ≤ 3000 -3000\le preorder[i], inorder[i]\le3000 −3000≤preorder[i],inorder[i]≤3000
- preorder和inorder均无重复元素
- inorder均出现在preorder
- preorder保证二叉树的前序遍历序列
- inorder保证二叉树的中序遍历序列
解题思路
本题使用递归
的方法解决。首先,对于任意一颗树而言,前序遍历的形式总是为[根节点, [左子树的前序遍历结果], [右子树的前序遍历结果]]
,即根节点总是前序遍历中的第一个节点,而中序遍历的形式为[[左子树的中序遍历结果],根节点, [右子树的中序遍历结果]]
。
只要在中序遍历中定位到根节点,就可以分别知道左子树和右子树中的节点数目。由于同一棵子树的前序遍历和中序遍历的长度显然是相同的,因此就可以对应到前序遍历的结果中,对上述形式中的所有左右括号进行定位。
这样,就知道了左子树的前序遍历和中序遍历结果,以及右子树的前序遍历和中序遍历结果,然后就可以递归地构造出左子树和右子树,然后再将着两棵子树拼接到根节点的左右位置上。
注意:为了快速对根节点进行定位,这里采用哈希表
,即哈希表中的每个键表示一个元素(节点的值),值表示其在中序遍历中的出现位置。在构造二叉树的过程中,对中序遍历的列表进行一次扫描,就可以构建哈希表。在搜索的时候时间复杂度就是O(1).
代码展示
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName :BuildTree.py
# @Time :2022/1/24 21:13
# @Author :PangXZ
# Leetcode: 105 从前序与中序遍历序列构造二叉树
# Definition for a binary tree node.
from typing import List
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
n = len(preorder)
# 构造哈希表,方便快速定位根节点
index_map = {element: i for i, element in enumerate(inorder)}
# 定义重构二叉树函数
def building(preorder_left: int, preorder_right: int, inorder_left: int, inorder_right: int):
if preorder_left > preorder_right or inorder_left > inorder_right:
return None
# 前序遍历中第一个位置为根节点
preorder_root = preorder_left
# 在中序遍历中寻找根节点
inorder_root = index_map[preorder[preorder_root]]
# 先建立二叉树的根节点
root = TreeNode(preorder[preorder_root])
# 得到左子树中的节点数目
size_left = inorder_root - inorder_left
# 递归地构造左子树,并链接到根节点
# 先序遍历中[从左边界+1开始的size_left]个元素对应了中序遍历中[从左边界到根节点定位-1]的元素
root.left = building(preorder_left + 1, preorder_left + size_left, inorder_left, inorder_root - 1)
# 递归地构造右子树,并连接到根节点
# 先序遍历中[从左边界+1+左子树节点数目开始,到右边界]的元素对应了中序遍历中[从根节点定位+1,到右边界]的元素
root.right = building(preorder_left + 1 + size_left, preorder_right, inorder_root + 1, inorder_right)
return root
return building(0, n - 1, 0, n - 1)
复杂度分析
- 时间复杂度: O ( n ) O(n) O(n),其中n是树中的节点个数
- 空间复杂度: O ( n ) O(n) O(n),除了返回答案需要n个空间之外,还需要使用n个空间存储哈希表,以及O(h)(h是树的高度)的空间表示递归时栈空间,这里h<n,所以总的空间复杂度为O(n).