The Problem
Given two integer arrays preorder and inorder where preorder is the preorder traversal of a binary tree and inorder is the inorder traversal of the same tree, construct and return the binary tree.
Examples
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
Input: preorder = [-1], inorder = [-1]
Output: [-1]
Constraints
- 1 <= preorder.length <= 3000
- inorder.length == preorder.length
- -3000 <= preorder[i], inorder[i] <= 3000
preorderandinorderconsist of unique values.- Each value of
inorderalso appears inpreorder. preorderis guaranteed to be the preorder traversal of the tree.inorderis guaranteed to be the inorder traversal of the tree.
Depth First Search Solution
func buildTree(_ preorder: [Int], _ inorder: [Int]) -> TreeNode? {
if preorder.isEmpty || inorder.isEmpty {
return nil
}
let rootVal = preorder[0]
let root = TreeNode(rootVal)
guard let mid = inorder.firstIndex(of: rootVal) else {
return nil
}
if mid > 0 {
root.left = buildTree(Array(preorder[1...mid]), Array(inorder[0..<mid]))
}
if mid < inorder.count - 1 {
root.right = buildTree(Array(preorder[mid + 1..<preorder.count]), Array(inorder[mid + 1..<inorder.count]))
}
return root
}
Explanation
We can solve this problem by understanding how preorder and inorder traversal work.
-
Preorder Traversal:
For preorder traversal, we start from theroot, then move to theleftsubtree, and after that to therightsubtree. For example, with the inputpreorder = [3,9,20,15,7], therootis always the first element in the array (in this case,3). Knowing this, we can recursively reconstruct theleftandrightsubtrees. -
Inorder Traversal:
Inorder traversal starts with theleftsubtree first, then moves to theroot, and after that to therightsubtree. For example, with the inputinorder = [9,3,15,20,7], every value to the left of therootbelongs to theleftsubtree, and every value to the right of therootbelongs to therightsubtree.
Using this information, we can find the root and then partition the arrays into subarrays to reconstruct the tree.
This is not a very efficient solution and takes O(n^2) time and space, but we can optimize it.
Time/Space Complexity
- Time complexity: O(n^2)
- Space complexity: O(n^2)
DFS Optimal Solution
func buildTree(_ preorder: [Int], _ inorder: [Int]) -> TreeNode? {
var indices: [Int: Int] = [:]
for (i, v) in inorder.enumerated() {
indices.updateValue(i, forKey: v)
}
var preIdx = 0
func dfs(_ l: Int, _ r: Int) -> TreeNode? {
if l > r {
return nil
}
let rootVal = preorder[preIdx]
preIdx += 1
let root = TreeNode(rootVal)
let mid = indices[rootVal]!
root.left = dfs(l, mid - 1)
root.right = dfs(mid + 1, r)
return root
}
return dfs(0, inorder.count - 1)
}
Explanation
We can solve this problem in a more optimal way by using the DFS algorithm and a hashmap. The logic behind the solution stays the same: we find the root value from the preorder array, then find the mid index in the inorder array, and build the tree using recursion.
Time/Space Complexity
- Time complexity: O(n)
- Space complexity: O(n)