Reconstructing Binary Trees from Traversal Sequences
The problem asks us to reconstruct a binary tree from two given traversal sequences of that tree.
Three Tree Traversals
- Preorder: Root - Left - Right
- Inorder: Left - Root - Right
- Postorder: Left - Right - Root
The Approach
We use divide-and-conquer based on the traversal patterns:
Preorder: (Root) (Left subtree …) (Right subtree …) Inorder: (Left subtree …) (Root) (Right subtree …) Postorder: (Left subtree …) (Right subtree …) (Root)
One sequence always gives us the root immediately. Once we have the root, we split the other sequence into left and right subtree parts. Then we recurse.
For example: Preorder + Inorder
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return createTree(preorder, inorder, 0, preorder.size()-1, 0, inorder.size()-1);
}
TreeNode* createTree(vector<int>& preorder, vector<int>& inorder, int ps, int pe, int is, int ie) {
if (ps > pe || is > ie) return NULL;
// Find root in preorder
int rootVal = preorder[ps];
int idx;
// Find split point in inorder using root value
for(idx=is;idx<=ie&&inorder[idx]!=rootVal;++idx) {}
TreeNode* troot = new TreeNode(rootVal);
// Recursively build left subtree
troot->left = createTree(preorder, inorder, ps+1, idx-is+ps, is, idx-1);
// Recursively build right subtree
troot->right = createTree(preorder, inorder, pe-(ie-idx)+1, pe, idx+1, ie);
return troot;
}Inorder + Postorder works the same way. Find the root, then split the inorder sequence. Use element counts to split the postorder sequence for recursion.
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
return createTree(inorder, postorder, 0, inorder.size()-1, 0, postorder.size()-1);
}
TreeNode* createTree(vector<int>& inorder, vector<int>& postorder, int is, int ie, int ps, int pe) {
if (is > ie || ps > pe) return NULL;
int rootVal = postorder[pe];
int idx;
for(idx=is;idx<=ie&&inorder[idx]!=rootVal;++idx) {}
TreeNode* troot = new TreeNode(rootVal);
troot->left = createTree(inorder, postorder, is, idx-1, ps, ps+idx-is-1);
troot->right = createTree(inorder, postorder, idx+1, ie, ps+idx-is, pe-1);
return troot;
}Preorder + Postorder both give us roots, but the splitting needs more care. We take the next element after the root in preorder and find its position in postorder. Then we split both sequences by matching lengths, following the same logic as before.
class Solution {
public:
TreeNode* constructFromPrePost(vector<int>& pre, vector<int>& post) {
int len = post.size();
for (int i = 0; i < len; i++) m[post[i]] = i;
return createTree(pre, post, 0, pre.size()-1, 0, post.size()-1);
}
TreeNode* createTree(vector<int>& pre, vector<int>& post, int pre_st, int pre_e, int po_st, int po_e) {
if (pre_st > pre_e || po_st > po_e) return NULL;
TreeNode* troot = new TreeNode(pre[pre_st]);
// Base case: return when only root remains
if (pre_st == pre_e) return troot;
int post_idx=m[pre[pre_st+1]];
troot->left = createTree(pre, post, pre_st+1, pre_st+1+post_idx-po_st, po_st, post_idx);
troot->right = createTree(pre, post, pre_st+2+post_idx-po_st, pre_e, post_idx+1, po_e-1);
return troot;
}
unordered_map<int, int> m;
};#recursion #divide-and-conquer #tree