Reference: LeetCode EPI 7.1
Difficulty: Medium
Problem
Given a binary tree, determine if it is a valid binary search tree (BST).
Assume a BST is defined as follows:
- The left subtree of a node contains only nodes with keys
less than
the node’s key. - The right subtree of a node contains only nodes with keys
greater than
the node’s key. - Both the left and right subtrees must also be binary search trees.
Example:
1 | 2 |
Follow up: $O(1)$ Space? No. Iteration methods need stacks too.
Analysis
Methods:
The naive approach would be to calculate the lower
and upper
values when visiting each node, but it turns out to be very inefficient because it does a lot of repeated calculation. In the worst case, time complexity would be $O(N^2)$.
One alternative might be to cache the largest and smallest keys at each node using a HashMap, which requires $O(N)$ additional storage for the cache.
- Recursion
- For a node $x$, all nodes in its left subtree should be satisfy the constraint
[L, x.val)
and all nodes in the other side should be satisfy(x.val, R]
. - Time: $O(N)$ since we visit each node exactly once.
- Space:
- Best: $O(\log{N})$ when the tree is bushy.
- Worst: $O(N)$
- For a node $x$, all nodes in its left subtree should be satisfy the constraint
- Iteration
- The above recursion could be converted into iteration with the help of three stacks.
- Time: $O(N)$
- Space: $O(N)$
- Inorder Traversal
- We can use the fact that an inorder traversal visits keys in sorted order. We can compare each node we visit with the previous node during the inorder traversal.
- Recursion
- Iteration
- Time: $O(N)$
- Space: $O(N)$ or $O(\log{N})$
- We can use the fact that an inorder traversal visits keys in sorted order. We can compare each node we visit with the previous node during the inorder traversal.
- BFS Manner
- When the property is violated at a node whose depth is small (close to the root), we can use this manner. Specifically, we use a queue, where each queue entry contains a node, as well as an upper bound and a lower bound on the keys.
- Time: $O(N)$
- Space: $O(N)$
Code
Test Case:
1 | [2,1,3] |
Recursion
Note:
- Use
Long.MIN_VALUE
rather thanInteger.MAX_VALUE
. But it’s better to usenull
. - Be careful of the traversal order. When invalidity is detected, we should return immediately.
- Notice the condition of the problem description:
Equality
is not allowed.
My original bad code:
It is not efficient because it is a postorder
traversal. If isValid
is false
, we should return immediately.
1 | public boolean isValidBST(TreeNode root) { |
Improvement:
1 | private boolean isBSTHelper(TreeNode x, long lower, long upper) { |
More Improvement:
Note: Rather than using extreme constants, we can use null
and modify the conditions.
1 | public boolean isValidBST(TreeNode root) { |
Iteration
Note:
- Check the stack before pushing values into it.
1 | Stack<TreeNode> stack = new Stack<>(); |
Inorder Traversal
Recursion
1 | public boolean isValidBST(TreeNode root) { |
Iteration
1 | private boolean inorderItr(TreeNode root) { |