Lc1008

05 August 2008

1008 Construct Binary Search Tree from Preorder Traversal

题目

Return the root node of a binary search tree that matches the given
1
preorder
traversal.

(Recall that a binary search tree is a binary tree where for every node, any descendant of
1
node.left
has a value
1
<
1
node.val
, and any descendant of
1
node.right
has a value
1
>
1
node.val
. Also recall that a preorder traversal displays the value of the
1
node
first, then traverses
1
node.left
, then traverses
1
node.right
.)

It’s guaranteed that for the given test cases there is always possible to find a binary search tree with the given requirements.

Example 1:

1
2
3
Input: [8,5,1,7,10,12]
Output: [8,5,10,1,7,null,12]

Constraints:

分析

给一个数组,是一个BST的先序遍历,构建返回对应的BST。

1) 方法1,类似105题-Construct Binary Tree from Preorder and Inorder Traversal的做法:在105题中,需要一个先序数组和一个中序数组,由于BST的中序遍历是递增的,可以根据先序遍历排序得到一个中序遍历的数组,随后使用分治的方法从先序遍历和中序遍历构造出二叉搜索树。

具体的方法是,先获取先序遍历中的第一个元素,它对应了二叉树的根节点,它在中序遍历中的位置为 x,那么中序遍历中 x 左边的所有元素都在根节点的左子树中,x右边的所有元素都在根节点的右子树中,这样根节点的左子树和右子树中的节点个数就都确定了。再回到先序遍历数组中,由于根节点的左子树和右子树在先序遍历中一定都是连续的一段,并且它们的个数已经确定,且左子树的先序遍历出现在右子树之前,那么就得到了根节点左子树和右子树对应的先序遍历。有了子树的先序遍历和中序遍历,就可以递归地构造子树了。过程如下图:

时间复杂度:对先序数组要排序O(NlogN);

空间复杂度:额外维护一个中序遍历数组,O(N);

2) 方法2,在方法1中在将先序遍历的数组排序得到中序遍历时,花费了O(Nlog⁡N)时间复杂度,但却没有得到任何额外的信息。也就是说,可以直接跳过生成中序遍历的步骤,根据先序遍历直接构造出二叉树。需要注意的是,由于题目中的二叉树是BST,因此根据先序遍历构造出的二叉树才是唯一的。

具体做法就是使用递归的方法,在扫描先序遍历数组的同时,构造出二叉树。递归时维护一个 (lower, upper)二元组,表示当前位置可以插入的节点的值的上下界。如果此时先序遍历位置的值处于上下界中,就将这个值作为新的节点插入到当前位置,并递归地处理当前位置的左右孩子的两个位置。否则回溯到当前位置的父节点。

时间复杂度:O(N),仅扫描前序遍历一次。

空间复杂度:O(N),用来存储二叉树。

3) 方法3,方法2中的递归过程用stack来模拟

时间复杂度:O(N),仅扫描前序遍历一次。

空间复杂度:O(N),用来存储栈和二叉树。

代码

方法1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    // 从前序数组的第一个元素开始
    int preorderIndex = 0;
    int[] preorderArr;
    HashMap<Integer, Integer> indexMap = new HashMap<>();

    public TreeNode bstFromPreorder(int[] preorder) {
        this.preorderArr = preorder;

        // 得到排序后的中序遍历数组
        int[] inorderArr = Arrays.copyOf(preorder, preorder.length);
        Arrays.sort(inorderArr);

        // build a hashmap value -> its index
        int idx = 0;
        for (Integer val : inorderArr) {
            indexMap.put(val, idx++);
        }
        // 类似105题,使用中序数组递归构建二叉树(BST)
        return helper(0, inorderArr.length);
    }

    public TreeNode helper(int in_left, int in_right) {
        // if there is no elements to construct subtrees
        if (in_left == in_right)
            return null;

        // 先序遍历的第一个元素是根节点
        int root_val = preorderArr[preorderIndex];
        TreeNode root = new TreeNode(root_val);

        // root splits inorder list
        // into left and right subtrees
        int index = indexMap.get(root_val);

        // recursion
        preorderIndex++;
        // build left subtree
        root.left = helper(in_left, index);
        // build right subtree
        root.right = helper(index + 1, in_right);
        return root;
    }
}

方法2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
    int idx = 0;
    int[] preorderArr;
    int len;

    public TreeNode bstFromPreorder(int[] preorder) {
        this.preorderArr = preorder;
        len = preorder.length;
        return helper(Integer.MIN_VALUE, Integer.MAX_VALUE);
    }

    public TreeNode helper(int lower, int upper) {
        // 如果所有先序遍历数组中的元素都用过了,说明BST已经构建完成
        if (idx == len) {
            return null;
        }

        // 得到当前元素
        int val = preorderArr[idx];
        
        // corner case,按照BST的特性,如果当前元素不能够被放在当前位置
        if (val < lower || val > upper) {
            return null;
        }

        // 构建当前元素
        idx++;
        TreeNode root = new TreeNode(val);
        并递归构建左右子树
        root.left = helper(lower, val);
        root.right = helper(val, upper);
        return root;
    }
}

方法3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
    public TreeNode bstFromPreorder(int[] preorder) {
        int len = preorder.length;
        if (len == 0) {
            return null;
        }

        TreeNode root = new TreeNode(preorder[0]);
        Deque<TreeNode> deque = new ArrayDeque<>();
        deque.push(root);

        for (int i = 1; i < len; i++) {
            // take the last element of the deque as a parent
            // and create a child from the next preorder element
            TreeNode node = deque.peek();
            TreeNode child = new TreeNode(preorder[i]);
            // adjust the parent 
            while (!deque.isEmpty() && deque.peek().val < child.val)
                node = deque.pop();

            // follow BST logic to create a parent-child link
            if (node.val < child.val) node.right = child;
            else node.left = child;
            // add the child into deque
            deque.push(child);
        }
        return root;
    }
}