Given preorder and inorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.

asked 20 Apr '11, 04:30

1337c0d3r's gravatar image

1337c0d3r ♦♦
376331131
accept rate: 0%


Hint:
A good way to attempt this question is to work backwards. Approach this question by drawing a binary tree, then list down its preorder and inorder traversal. As most binary tree problems, you want to solve this recursively.

About Duplicates:
In this solution, we will assume that duplicates are not allowed in the binary tree. Why?

Consider the following case:

preorder = [7, 7]
inorder =  [7, 7]

We can construct the following trees which are both perfectly valid solutions.

      7                     7
     /           or          \
    7                         7

Clearly, there would be ambiguity in constructing the tree if duplicates were allowed.

Solution:
Let us look at this example tree.

         _______7______
        /              \
     __10__          ___2
    /      \        /
    4       3      _8
             \    /
              1  11

The preorder and inorder traversals for the binary tree above is:

preorder = [7, 10, 4, 3, 1, 2, 8, 11]
inorder =  [4, 10, 3, 1, 7, 11, 8, 2]

The crucial observation to this problem is the tree's root always coincides with the first element in preorder traversal. This must be true because in preorder traversal you always traverse the root node before its children. The root node's value appear to be 7 from the binary tree above.

We easily find that 7 appears as the 4th index in the inorder sequence. (Notice that earlier we assumed that duplicates are not allowed in the tree, so there would be no ambiguity). For inorder traversal, we visit the left subtree first, then root node, and followed by the right subtree. Therefore, all elements left of 7 must be in the left subtree and all elements to the right must be in the right subtree.

We see a clear recursive pattern from the above observation. After creating the root node (7), we construct its left and right subtree from inorder traversal of [4, 10, 3, 1] and [11, 8, 2] respectively. We also need its corresponding preorder traversal which could be found in a similar fashion. If you remember, preorder traversal follows the sequence of root node, left subtree and followed by right subtree. Therefore, the left and right subtree's postorder traversal must be [10, 4, 3, 1] and [2, 8, 11] respectively. Since the left and right subtree are binary trees in their own right, we can solve recursively!

We left out some details on how we search the root value's index in the inorder sequence. How about a simple linear search? If we assume that the constructed binary tree is always balanced, then we can guarantee the run time complexity to be O(N log N), where N is the number of nodes. However, this is not necessarily the case and the constructed binary tree can be skewed to the left/right, which has the worst complexity of O(N2).

A more efficient way is to eliminate the search by using an efficient look-up mechanism such as hash table. By hashing an element's value to its corresponding index in the inorder sequence, we can do look-ups in constant time. Now, we need only O(N) time to construct the tree, which theoretically is the most efficient way.

For illustration purpose, the below code uses a simple array for table look-up, which is restricted to elements of 0 to 255 only. You should be able to extend it easily to use a hash table.

const int MAX = 256;
// a fast lookup for inorder's element -> index
// binary tree's element must be in the range of [0, MAX-1]
int mapIndex[MAX];
void mapToIndices(int inorder[], int n) {
    for (int i = 0; i < n; i++) {
        assert(0 <= inorder[i] && inorder[i] <= MAX-1);
        mapIndex[inorder[i]] = i;
    }
}

// precondition: mapToIndices must be called before entry
Node *buildInorderPreorder(int in[], int pre[], int n, int offset) {
    assert(n >= 0);
    if (n == 0) return NULL;
    int rootVal = pre[0];
    int i = mapIndex[rootVal]-offset;  // the divider's index
    Node *root = new Node(rootVal);
    root->left = buildInorderPreorder(in, pre+1, i, offset);
    root->right = buildInorderPreorder(in+i+1, pre+i+1, n-i-1, offset+i+1);
    return root;
}

Now, if we are given inorder and postorder traversal, can we construct the binary tree?

The answer is yes, using very similar approach as above. Please see question Construct Binary Tree from Inorder and Postorder Traversal for more details.

Further Thoughts:

  1. If we are given preorder and postorder traversal, can we construct the binary tree? Why or why not?
  2. Given preorder, inorder, and postorder traversal, how can you verify if these traversals are referring to the exact same binary tree?
  3. Remember from my earlier post: Serialization/Deserialization of a Binary Tree? It is trivial to see this as an alternative method to serialize/deserialize a binary tree. :)
link

answered 20 Apr '11, 04:30

1337c0d3r's gravatar image

1337c0d3r ♦♦
376331131
accept rate: 0%

nice answer

(21 Jan, 20:03) sakopqiu sakopqiu's gravatar image

"Therefore, the left and right subtree's postorder traversal must be [10, 4, 3, 1] and [2, 8, 11] respectively." It should be preorder, not postorder.

(27 Jan, 19:26) jiemw jiemw's gravatar image
/**Passed both small and large judge.
Not so many surprise here, just pay extra attention to the boundary indices.
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* build(vector<int> const& preorder, int const pre_first, int const pre_last,
      vector<int> const& inorder, int const in_first, int const in_last){
          if (in_first > in_last) return NULL;
          TreeNode* root = new TreeNode(preorder[pre_first]);
          if (in_first == in_last) return root;
          size_t const r_in = distance(inorder.begin(), 
            find(inorder.begin()+in_first, inorder.begin()+in_last, root->val));
          size_t const sizeLeft = r_in - in_first;//size of left subtree
          root->left = build(preorder, pre_first+1, pre_first+sizeLeft-1,
            inorder, in_first, r_in-1);
          root->right = build(preorder, pre_first+sizeLeft+1, pre_last,
            inorder, r_in+1, in_last);
          return root;
      }
    TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        return build(preorder, 0, preorder.size()-1,
          inorder, 0, inorder.size()-1);

    }
};
link

answered 31 Dec '12, 09:21

cppInitiator's gravatar image

cppInitiator
8623
accept rate: 0%

public TreeNode buildTree(int[] preorder, int[] inorder) {
    if (preorder.length == 0) {
        return null;
    }

    // the first item in preorder is root
    TreeNode root = new TreeNode(preorder[0]);
    if (preorder.length == 1) {
        return root;
    }

    int i = 0;
    for (; inorder[i] != root.val; i++)
        ;

    if (i > 0) {
        root.left = buildTree(Arrays.copyOfRange(preorder, 1, i + 1),
                Arrays.copyOfRange(inorder, 0, i));
    }
    if (i < inorder.length - 1) {
        root.right = buildTree(Arrays.copyOfRange(preorder, i + 1, preorder.length),
                Arrays.copyOfRange(inorder, i + 1, inorder.length));
    }

    return root;
}
link

answered 17 Jan, 17:30

lvlv's gravatar image

lvlv
664
accept rate: 0%

std::unordered_map<int,int> index_map;

void buildMap(vector<int> &inorder) {
    index_map.clear();
    for (int i = 0; i < inorder.size(); i++) {
        index_map[inorder[i]] = i;
    }
}

TreeNode *buildTreeInOrder(vector<int> &preorder, int pre_offset,
        vector<int> &inorder, int in_offset, int len) {
    if (!len)
        return NULL;

    cout << pre_offset << ", " << in_offset << ", " << len << endl;

    TreeNode *root = new TreeNode(preorder[pre_offset]);
    int i = index_map[preorder[pre_offset]];

    root->left = buildTreeInOrder(preorder,
            pre_offset+1, inorder, in_offset, i-in_offset);
    root->right = buildTreeInOrder(preorder,
            pre_offset+(i-in_offset)+1, inorder, in_offset+(i-in_offset)+1, len-(i-in_offset)-1);

    return root;
}

TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {
    // Start typing your C/C++ solution below
    // DO NOT write int main() function
    if (preorder.size() == 0 && inorder.size() == 0) {
        return NULL;
    }

    buildMap(inorder);
    return buildTreeInOrder(preorder, 0, inorder, 0, preorder.size());
}
link

answered 14 Mar, 20:45

whitebluff's gravatar image

whitebluff
111
accept rate: 0%

Your answer
toggle preview

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • Indent code by 4 spaces.
  • link:[text](http://url.com/ "Title")
  • image?![alt text](/path/img.jpg "Title")
  • numbered list: 1. Foo 2. Bar
  • to add a line break simply add two spaces to where you would like the new line to be.
  • basic HTML tags are also supported

Tags:

×132
×20

Asked: 20 Apr '11, 04:30

Seen: 607 times

Last updated: 14 Mar, 20:45