You are given two integer arrays inorder and postorder where inorder is the inorder traversal of a binary tree and postorder is the postorder traversal of the same tree, construct and return the binary tree.
Please upgrade to NeetCode Pro to view company tags.
Prerequisites
Before attempting this problem, you should be comfortable with:
Binary Tree Traversals - Understanding inorder (left-root-right) and postorder (left-right-root) traversal orders
Recursion / DFS - Building trees recursively by dividing the problem into subtrees
Hash Maps - Mapping values to indices for O(1) lookup of root positions in inorder array
Array Slicing - Dividing arrays into subarrays for left and right subtree construction
1. Depth First Search
Intuition
The last element of postorder is always the root. Once we identify the root, we find it in the inorder array. Everything to the left of the root in inorder belongs to the left subtree, and everything to the right belongs to the right subtree. We recursively apply this logic, slicing the arrays accordingly.
Algorithm
If postorder or inorder is empty, return null.
Create the root node using the last element of postorder.
Find the index mid of the root value in inorder.
Recursively build the left subtree using inorder[0:mid] and postorder[0:mid].
Recursively build the right subtree using inorder[mid+1:] and postorder[mid:-1].
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/publicclassSolution{publicTreeNodeBuildTree(int[] inorder,int[] postorder){if(postorder.Length ==0|| inorder.Length ==0){returnnull;}int rootVal = postorder[postorder.Length -1];TreeNode root =newTreeNode(rootVal);int mid = Array.IndexOf(inorder, rootVal);int[] leftInorder = inorder[..mid];int[] rightInorder = inorder[(mid +1)..];int[] leftPostorder = postorder[..mid];int[] rightPostorder = postorder[mid..^1];
root.left =BuildTree(leftInorder, leftPostorder);
root.right =BuildTree(rightInorder, rightPostorder);return root;}}
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/funcbuildTree(inorder []int, postorder []int)*TreeNode {iflen(postorder)==0||len(inorder)==0{returnnil}
rootVal := postorder[len(postorder)-1]
root :=&TreeNode{Val: rootVal}
mid :=0for i, v :=range inorder {if v == rootVal {
mid = i
break}}
root.Left =buildTree(inorder[:mid], postorder[:mid])
root.Right =buildTree(inorder[mid+1:], postorder[mid:len(postorder)-1])return root
}
/**
* Example:
* var ti = TreeNode(5)
* var v = ti.`val`
* Definition for a binary tree node.
* class TreeNode(var `val`: Int) {
* var left: TreeNode? = null
* var right: TreeNode? = null
* }
*/class Solution {funbuildTree(inorder: IntArray, postorder: IntArray): TreeNode?{if(postorder.isEmpty()|| inorder.isEmpty()){returnnull}val rootVal = postorder.last()val root =TreeNode(rootVal)val mid = inorder.indexOf(rootVal)
root.left =buildTree(
inorder.sliceArray(0 until mid),
postorder.sliceArray(0 until mid))
root.right =buildTree(
inorder.sliceArray(mid +1 until inorder.size),
postorder.sliceArray(mid until postorder.size -1))return root
}}
/**
* Definition for a binary tree node.
* public class TreeNode {
* public var val: Int
* public var left: TreeNode?
* public var right: TreeNode?
* public init() { self.val = 0; self.left = nil; self.right = nil; }
* public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; }
* public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) {
* self.val = val
* self.left = left
* self.right = right
* }
* }
*/classSolution{funcbuildTree(_ inorder:[Int],_ postorder:[Int])->TreeNode?{if postorder.isEmpty || inorder.isEmpty {returnnil}let rootVal = postorder.last!let root =TreeNode(rootVal)let mid = inorder.firstIndex(of: rootVal)!
root.left=buildTree(Array(inorder[0..<mid]),Array(postorder[0..<mid]))
root.right=buildTree(Array(inorder[(mid +1)...]),Array(postorder[mid..<(postorder.count -1)]))return root
}}
Time & Space Complexity
Time complexity: O(n2)
Space complexity: O(n)
2. Hash Map + Depth First Search
Intuition
The previous approach has O(n) lookup time when finding the root in inorder. By precomputing a hash map that stores each value's index in inorder, we achieve O(1) lookups. We also avoid creating new arrays by using index boundaries instead.
Algorithm
Build a hash map mapping each value in inorder to its index.
Maintain a pointer postIdx starting at the end of postorder.
Define dfs(l, r) that builds the tree for the inorder range [l, r]:
If l > r, return null.
Pop the current root from postorder (decrement postIdx), create a node.
Find the root's index in inorder using the hash map.
Build the right subtree first (since we're processing postorder from the end), then the left subtree.
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/publicclassSolution{int[] postorder;Dictionary<int,int> inorderIdx;int idx;publicTreeNodeBuildTree(int[] inorder,int[] postorder){this.postorder = postorder;
inorderIdx =newDictionary<int,int>();for(int i =0; i < inorder.Length; i++){
inorderIdx[inorder[i]]= i;}
idx = postorder.Length -1;returnDfs(0, inorder.Length -1);}privateTreeNodeDfs(int l,int r){if(l > r)returnnull;TreeNode root =newTreeNode(postorder[idx--]);int i = inorderIdx[root.val];
root.right =Dfs(i +1, r);
root.left =Dfs(l, i -1);return root;}}
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/funcbuildTree(inorder []int, postorder []int)*TreeNode {
inorderIdx :=make(map[int]int)for i, v :=range inorder {
inorderIdx[v]= i
}
postIdx :=len(postorder)-1var dfs func(l, r int)*TreeNode
dfs =func(l, r int)*TreeNode {if l > r {returnnil}
root :=&TreeNode{Val: postorder[postIdx]}
postIdx--
idx := inorderIdx[root.Val]
root.Right =dfs(idx+1, r)
root.Left =dfs(l, idx-1)return root
}returndfs(0,len(inorder)-1)}
/**
* Example:
* var ti = TreeNode(5)
* var v = ti.`val`
* Definition for a binary tree node.
* class TreeNode(var `val`: Int) {
* var left: TreeNode? = null
* var right: TreeNode? = null
* }
*/class Solution {privatelateinitvar inorderIdx: Map<Int, Int>privatevar postIdx =0privatelateinitvar postorder: IntArray
funbuildTree(inorder: IntArray, postorder: IntArray): TreeNode?{this.postorder = postorder
inorderIdx = inorder.withIndex().associate{ it.value to it.index }
postIdx = postorder.size -1returndfs(0, inorder.size -1)}privatefundfs(l: Int, r: Int): TreeNode?{if(l > r)returnnullval root =TreeNode(postorder[postIdx--])val idx = inorderIdx[root.`val`]!!
root.right =dfs(idx +1, r)
root.left =dfs(l, idx -1)return root
}}
/**
* Definition for a binary tree node.
* public class TreeNode {
* public var val: Int
* public var left: TreeNode?
* public var right: TreeNode?
* public init() { self.val = 0; self.left = nil; self.right = nil; }
* public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; }
* public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) {
* self.val = val
* self.left = left
* self.right = right
* }
* }
*/classSolution{privatevar inorderIdx:[Int:Int]=[:]privatevar postIdx =0privatevar postorder:[Int]=[]funcbuildTree(_ inorder:[Int],_ postorder:[Int])->TreeNode?{self.postorder = postorder
for(i, v)in inorder.enumerated(){
inorderIdx[v]= i
}
postIdx = postorder.count -1returndfs(0, inorder.count -1)}privatefuncdfs(_ l:Int,_ r:Int)->TreeNode?{if l > r {returnnil}let root =TreeNode(postorder[postIdx])
postIdx -=1let idx = inorderIdx[root.val]!
root.right=dfs(idx +1, r)
root.left=dfs(l, idx -1)return root
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n)
3. Depth First Search (Optimal)
Intuition
We can eliminate the hash map by using a boundary-based approach. Instead of explicitly finding the root's position, we pass a "limit" value that tells us when to stop building the current subtree. When we encounter the limit in inorder, we know the current subtree is complete. This approach processes both arrays from right to left.
Algorithm
Initialize pointers postIdx and inIdx both at the last index.
Define dfs(limit) that builds the subtree bounded by limit:
If postIdx < 0, return null.
If inorder[inIdx] equals limit, decrement inIdx and return null (subtree boundary reached).
Create a node with postorder[postIdx], decrement postIdx.
Build the right subtree with limit set to the current node's value.
Build the left subtree with the original limit.
Return the node.
Call dfs with an impossible limit value (like infinity) and return the result.
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/publicclassSolution{int postIdx, inIdx;int[] inorder, postorder;publicTreeNodeBuildTree(int[] inorder,int[] postorder){this.inorder = inorder;this.postorder = postorder;
postIdx = inIdx = postorder.Length -1;returnDfs(int.MaxValue);}privateTreeNodeDfs(int limit){if(postIdx <0)returnnull;if(inorder[inIdx]== limit){
inIdx--;returnnull;}TreeNode root =newTreeNode(postorder[postIdx--]);
root.right =Dfs(root.val);
root.left =Dfs(limit);return root;}}
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/funcbuildTree(inorder []int, postorder []int)*TreeNode {
postIdx :=len(postorder)-1
inIdx :=len(inorder)-1var dfs func(limit int)*TreeNode
dfs =func(limit int)*TreeNode {if postIdx <0{returnnil}if inorder[inIdx]== limit {
inIdx--returnnil}
root :=&TreeNode{Val: postorder[postIdx]}
postIdx--
root.Right =dfs(root.Val)
root.Left =dfs(limit)return root
}returndfs(math.MaxInt32)}
/**
* Example:
* var ti = TreeNode(5)
* var v = ti.`val`
* Definition for a binary tree node.
* class TreeNode(var `val`: Int) {
* var left: TreeNode? = null
* var right: TreeNode? = null
* }
*/class Solution {privatevar postIdx =0privatevar inIdx =0privatelateinitvar inorder: IntArray
privatelateinitvar postorder: IntArray
funbuildTree(inorder: IntArray, postorder: IntArray): TreeNode?{this.inorder = inorder
this.postorder = postorder
postIdx = postorder.size -1
inIdx = inorder.size -1returndfs(Int.MAX_VALUE)}privatefundfs(limit: Int): TreeNode?{if(postIdx <0)returnnullif(inorder[inIdx]== limit){
inIdx--returnnull}val root =TreeNode(postorder[postIdx--])
root.right =dfs(root.`val`)
root.left =dfs(limit)return root
}}
/**
* Definition for a binary tree node.
* public class TreeNode {
* public var val: Int
* public var left: TreeNode?
* public var right: TreeNode?
* public init() { self.val = 0; self.left = nil; self.right = nil; }
* public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; }
* public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) {
* self.val = val
* self.left = left
* self.right = right
* }
* }
*/classSolution{privatevar postIdx =0privatevar inIdx =0privatevar inorder:[Int]=[]privatevar postorder:[Int]=[]funcbuildTree(_ inorder:[Int],_ postorder:[Int])->TreeNode?{self.inorder = inorder
self.postorder = postorder
postIdx = postorder.count -1
inIdx = inorder.count -1returndfs(Int.max)}privatefuncdfs(_ limit:Int)->TreeNode?{if postIdx <0{returnnil}if inorder[inIdx]== limit {
inIdx -=1returnnil}let root =TreeNode(postorder[postIdx])
postIdx -=1
root.right=dfs(root.val)
root.left=dfs(limit)return root
}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(n) for recursion stack.
Common Pitfalls
Building Left Subtree Before Right When Using Postorder Index
When processing postorder from right to left, you must build the right subtree before the left subtree. The postorder array is structured as [left, right, root], so when traversing backwards, we encounter right subtree elements first.
# Wrong: Building left before right
root.left = dfs(l, idx -1)
root.right = dfs(idx +1, r)# postIdx is now wrong# Correct: Build right first
root.right = dfs(idx +1, r)
root.left = dfs(l, idx -1)
Off-by-One Errors in Array Slicing
When slicing arrays for recursive calls, it's easy to miscalculate the postorder boundaries. The left subtree has mid elements, so postorder[:mid] is correct for the left subtree, and postorder[mid:-1] (excluding the root) is correct for the right subtree.
# Wrong: Including the root in right subtree
root.right = self.buildTree(inorder[mid +1:], postorder[mid:])# Correct: Exclude the root (last element)
root.right = self.buildTree(inorder[mid +1:], postorder[mid:-1])
Assuming Preorder Logic Works for Postorder
In preorder traversal, the root is at the beginning of the array, but in postorder, the root is at the end. Using postorder[0] instead of postorder[-1] as the root will produce an incorrect tree.