Construct Binary Tree from Preorder and Inorder Traversal

update Aug 29, 2017 23:07

LeetCode

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

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

Basic Idea:

通过观察,我们可以发现 preorder 遍历的结果的第一个数一定是 root,而在 inorder list 中,root 会把左右两子树一分为二,利用这种性质,我们可以得到一种递归解法:

  • 对于输入inorder lst 和 preorder lst,先标记 preorde[0] 作为 root,然后在 inorder 中查找 root 的位置;

  • 这样就将两个 list 分为了两部分,分表为当前root左右子树的元素,对左右子树递归执行;

例如,对于input:

preorder: [1,2,4,5,7,3,6]
inorder:  [4,2,7,5,1,3,6]

我们可以看到,对于以 1 为 root 的树,我们可以用 1 将 inorder 分成两部分,递归传给下层函数的参数分别为:

left:                        right:
    preorder: [2,3,5,7]            preorder: [3,6]
    inorder   [4,2,7,5]            inorder:  [3,6]

Java Code:

    class Solution {
        public TreeNode buildTree(int[] preorder, int[] inorder) {
            return helper(preorder, inorder);
        }
        private TreeNode helper(int[] preorder, int[] inorder) {
            if (preorder.length == 0 ) return null;
            if (preorder.length == 1) return new TreeNode(preorder[0]);
            TreeNode root = new TreeNode(preorder[0]);
            int root_idx = indexOf(inorder, preorder[0]);
            int l_subtree_size = root_idx;
            int r_subtree_size = inorder.length - root_idx - 1;
            if (l_subtree_size > 0) {
                root.left = helper(Arrays.copyOfRange(preorder, 1, l_subtree_size + 1), Arrays.copyOfRange(inorder, 0, root_idx));
            }
            if (r_subtree_size > 0) {
                root.right = helper(Arrays.copyOfRange(preorder, l_subtree_size + 1, preorder.length),
                                    Arrays.copyOfRange(inorder, root_idx + 1, inorder.length));
            }
            return root;
        }
        private int indexOf(int[] arr, int target) {
            for (int i = 0; i < arr.length; ++i) {
                if (arr[i] == target) return i;
            }
            return -1;
        }
    }

Python Code:

    class Solution(object):
        def buildTree(self, preorder, inorder):
            """
            :type preorder: List[int]
            :type inorder: List[int]
            :rtype: TreeNode
            """
            if not preorder:
                return None
            if len(preorder) == 1:
                return TreeNode(preorder[0])
            root = TreeNode(preorder[0])
            root_idx = inorder.index(preorder[0])
            left_size = root_idx
            right_size = len(inorder) - root_idx - 1
            if left_size:
                root.left = self.buildTree(preorder[1 : left_size + 1], inorder[0 : root_idx])
            if right_size:
                root.right = self.buildTree(preorder[left_size + 1 :], inorder[root_idx + 1 :])
            return root

update Dec 24, 2017 3:01

Update:

时间复杂度: 因为我们每次在inorder list中找root位置时候都要线性搜索,时间复杂度为 O(n^2),可以预处理一下,用一个 map 存放所有 node 在 inorder list 中的 index,这样时间复杂度变为 O(n),但空间复杂度从 O(1) 变为 O(n);

更新一个优化了时间复杂度的 Java 实现:

    class Solution {
        private Map<Integer, Integer> map;
        public TreeNode buildTree(int[] preorder, int[] inorder) {
            this.map = new HashMap<>();
            for (int idx = 0; idx < inorder.length; ++idx) {
                map.put(inorder[idx], idx);
            }
            return helper(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1);
        }

        // return 为建成的tree的root,parameter是用来模拟传入了两个subarray`
        private TreeNode helper(int[] preorder, int[] inorder, int pstart, int pend, int istart, int iend) {
            if (pstart > pend) return null;
            TreeNode root = new TreeNode(preorder[pstart]);
            if (pstart == pend) return root;
            int rootIndex = map.get(root.val);
            int leftSize = rootIndex - istart;
            // 这里主要是index各种计算,看不懂的话可以画图举例理解
            root.left = helper(preorder, inorder, pstart + 1, pstart + leftSize, istart, rootIndex - 1);
            root.right = helper(preorder, inorder, pstart + leftSize + 1, pend, rootIndex + 1, iend);
            return root;
        }
    }

update 2018-06-06 22:41:13

Update C++ Solution

运用之前的思路,用C++写了一个解法:

class Solution {
    TreeNode* helper(unordered_map<int, int>& idxs, const vector<int>& preorder,
                     const vector<int>& inorder, int pre_start, int pre_end, int in_start, int in_end) {
        if (pre_start > pre_end || in_start > in_end) return nullptr;
        int root_val = preorder[pre_start];
        int root_inorder_idx = idxs[root_val];
        int leftSize = root_inorder_idx - in_start;
        int rightSize = in_end - root_inorder_idx;

        TreeNode* root = new TreeNode(root_val);
        root->left = helper(idxs, preorder, inorder, pre_start + 1, pre_start + leftSize, in_start, root_inorder_idx - 1);
        root->right = helper(idxs, preorder, inorder, pre_start + leftSize + 1, pre_end, root_inorder_idx + 1, in_end);

        return root;
    }
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if (preorder.empty()) return nullptr;
        unordered_map<int, int> idxs;
        for (int i = 0; i < inorder.size(); ++i) {
            idxs.insert(make_pair(inorder[i], i));
        }
        int size = preorder.size();
        return helper(idxs, preorder, inorder, 0, size - 1, 0, size - 1);
    }
};

update 2021-06-08

时隔三年的再一次更新,希望这次也会有好结果。

可以注意到,事实上没有必要维持每次递归时候preorder数组的右边界,因为我们每次都只需要取preorder中的第一个作为这次的root,所以只需要传入每次preorder的起点。

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return construct(preorder, inorder, 0, 0, inorder.length - 1);
    }

    private TreeNode construct(int[] preorder, int[] inorder, int currPre, int left, int right) {
        if (currPre == preorder.length || left > right) {
            return null;
        }
        TreeNode root = new TreeNode(preorder[currPre]);
        int indexInorder = left;
        while (inorder[indexInorder] != preorder[currPre]) {
            indexInorder++;
        }
        root.left = construct(preorder, inorder, currPre + 1, left, indexInorder - 1);
        root.right = construct(preorder, inorder, currPre + indexInorder - left + 1, indexInorder + 1, right);
        return root;
    }
}

Last updated