Topics › Binary Search Trees › Practice Problems
Floor in a Binary Search Tree
Java source ↗ Edit this article ↗
Jump to code:
Problem
Given the root of a binary search tree and an integer x, return the floor of x: the largest key in the tree that is less than or equal to x. If every key is larger than x, return -1. Keys are non-negative, so -1 is never a real key.
Constraints: up to 10^5 nodes; keys and x are between 0 and 10^9.
Example 1
8
/ \
4 12
/ \ / \
2 6 10 14
x = 11 answer = 10
The keys not above 11 are 2, 4, 6, 8, 10; the largest is 10.
Example 2
same tree, x = 1 answer = -1
The smallest key is 2, which is already above 1.
If x itself is in the tree, the answer is x.
Approach 1: Brute force, look at every node
Intuition. The floor is “the maximum over all keys that are <= x”. Without using the ordering, we can visit every node, keep the ones that qualify and take the largest. Because keys are non-negative, -1 works as “nothing found yet”: it loses to every real key in a max.
Algorithm.
- For an empty subtree, return
-1. - The node’s own contribution is its key if
key <= x, otherwise-1. - Return the maximum of that, the floor found in the left subtree, and the floor found in the right subtree.
static int bruteForce(TreeNode root, int x) {
if (root == null) return -1;
int best = root.val <= x ? root.val : -1;
best = Math.max(best, bruteForce(root.left, x));
best = Math.max(best, bruteForce(root.right, x));
return best;
}
Complexity. Time O(n): every node is visited. Space O(h) for the recursion stack.
Approach 2: Recursive descent using the BST property
Intuition. Compare x with the root.
- Equal: the floor is
x; nothing can be closer. - Root is larger than
x: the root does not qualify, and neither does anything in its right subtree, since those keys are larger still. The floor, if any, is in the left subtree. - Root is smaller than
x: the root qualifies. A better answer would have to be larger than the root and still<= x, and the only larger keys are in the right subtree. So ask the right subtree for its floor. If it has one, that answer is larger than the root and wins; if it returns-1, the root is the answer.
Each call discards one subtree, so only one path is explored.
Algorithm.
- If the node is
null, return-1. - If
root.val == x, returnx. - If
root.val > x, return the floor of the left subtree. - Otherwise compute
closer= floor of the right subtree; returncloserif it is not-1, elseroot.val.
static int recursive(TreeNode root, int x) {
if (root == null) return -1;
if (root.val == x) return x;
if (root.val > x) return recursive(root.left, x); // root and its right subtree are all too big
int closer = recursive(root.right, x); // root fits; something closer may be on its right
return closer != -1 ? closer : root.val;
}
Complexity. Time O(h), Space O(h) for the recursion stack.
Approach 3: Optimal, iterative descent
Intuition. Turn the recursion into a loop by carrying the “best candidate so far” forward instead of combining answers on the way back. Walk down from the root. Whenever cur.val < x, cur is a valid floor, so record it and move right to look for something closer. Whenever cur.val > x, move left. Stop on an exact match or when the walk falls off the tree.
Why is it enough to overwrite the candidate instead of taking a maximum? After recording cur.val we move into the right subtree of cur, and every key there is larger than cur.val. So each newly recorded candidate is larger than all previous ones, which makes the latest candidate the best.
Algorithm.
- Set
floor = -1andcur = root. - While
curis notnull:- if
cur.val == x, returnx; - if
cur.val < x, setfloor = cur.valand move right; - otherwise move left.
- if
- Return
floor.
static int optimal(TreeNode root, int x) {
int floor = -1;
TreeNode cur = root;
while (cur != null) {
if (cur.val == x) return x;
if (cur.val < x) {
floor = cur.val; // candidate; every later candidate is larger
cur = cur.right;
} else {
cur = cur.left;
}
}
return floor;
}
Dry run on Example 1 with x = 11:
| cur | comparison | action | floor |
|---|---|---|---|
| 8 | 8 < 11 | record, go right | 8 |
| 12 | 12 > 11 | go left | 8 |
| 10 | 10 < 11 | record, go right | 10 |
| null | stop | 10 |
And with x = 1: at 8 go left, at 4 go left, at 2 go left, fall off. No candidate was ever recorded, so the answer is -1.
Complexity. Time O(h), which is O(log n) for a balanced tree and O(n) for a chain. Space O(1).
How this relates to binary search
On a sorted array the floor is found with binary search: keep the last index whose value is <= x while halving the range. The tree walk is the same algorithm. The current node is the “middle”, its left subtree is the lower half, its right subtree the upper half, and recording a candidate before moving right is exactly “remember this index, then search the right half”. The only difference is that a tree’s halves may be unequal in size, which is why the cost is the height rather than log2 n.
Edge cases and pitfalls
- Exact match. Return immediately. If you drop the equality check, the
elsebranch treats an equal key as too big, moves left and misses it. xbelow every key. The answer is-1. The test with the left chain5, 4, 3, 2andx = 1walks all the way down without recording anything.xabove every key. The answer is the maximum key: the walk keeps going right and records every node on the right edge.- Empty tree. All three methods return
-1. - Key
0. A floor of0is a real answer. The-1sentinel only works because keys are non-negative; with arbitrary keys, return anIntegerand usenull. - Ancestor answers. For
x = 9the floor is the root, recorded at the first step. The walk continues for two more levels and finds nothing better, which is fine: the candidate is kept.
Related problems
- Floor and Ceil in a BST computes the ceil alongside the floor with the mirror-image walk.
- Floor and Ceil in Sorted Array is the array version solved with binary search.
- Search in a Binary Search Tree is the same walk without candidates.
- Inorder Successor/Predecessor in BST uses the same “record and turn” idea with strict comparisons.
- Introduction to BST covers the property that lets each comparison discard a subtree.