Ceil in a BST is a clean little tree problem that rewards you for actually using the binary-search-tree ordering instead of treating the tree like a plain bag of numbers. The whole solution is one walk from the root to a leaf.
Problem. Given a binary search tree and an integer key, return the ceil of the key: the
smallest node value that is greater than or equal to key. If no such value exists, return None.
Example: for the tree with root 8 (children 4 and 12; 4 has children 2 and 6; 6 has child 5; 12 has
child 10) and key = 5, the answer is 5 (it is present and is the smallest value that is at least 5).
The slow way first
The obvious idea: collect every value in the tree, then scan for the smallest one that is >= key. An in-order traversal visits all n nodes and gives them in sorted order, so that is O(n) time and O(n) space for the list. It works, but it throws away the very thing that makes a BST special — its ordering.
The question to ask: at any node, do I even need to look at both subtrees? In a BST, no. The ordering tells me which way to go.
The idea: let the ordering steer you
Walk down from the root, keeping the best candidate seen so far. At each node:
- If
node.val >= key, this node qualifies as a ceil. Record it, then go left — anything smaller that still qualifies can only be in the left subtree. - If
node.val < key, this node is too small to ever be the ceil, so go right to find larger values.
When you walk off the tree (hit None), the last value you recorded is the answer.
Because we always move to exactly one child, the whole thing is a single root-to-leaf path — no recursion stack, no backtracking.
Walk through it
Step through the animation. The node pointer rides the current node. At 8 (>= 5) we record 8 and go left. At 4 (< 5) we go right, candidate unchanged. At 6 (>= 5) we record the better candidate 6 and go left. At 5 (>= 5) we record 5 and go left, but 5 has no left child, so node becomes None and we stop. The last candidate, 5, is the answer.
Pseudocode
ceil = None
node = root
while node is not None:
if node.val >= key:
ceil = node.val # candidate: smallest qualifying so far
node = node.left # try for something even closer to key
else:
node = node.right # node too small, larger values are right
return ceilThe Python solution
def find_ceil(root, key):
ceil = None
node = root
while node is not None:
if node.val >= key:
ceil = node.val
node = node.left
else:
node = node.right
return ceilceilholds the best candidate found so far, starting asNone.- We walk with
node, beginning at the root. - Lines 5 to 7 are the heart: when
node.val >= key, this value qualifies, so we record it and steer left toward smaller qualifying values. - The
elsebranch handlesnode.val < key: the node cannot be the ceil, so we go right for larger values. - When
nodebecomesNone, the loop ends and the last recordedceilis returned.
Complexity
| Case | Time | Notes |
|---|---|---|
| In-order + scan | O(n) (moderate) | visits every node |
| Guided walk (this solution) | O(h) (moderate) | one root-to-leaf path |
O(1) (fast)Here h is the height of the tree — O(log n) if it is balanced, O(n) in the worst (degenerate) case. The iterative walk uses only a couple of variables, so extra space is O(1).
When this pattern shows up
Whenever a BST problem asks for a predecessor, successor, floor, ceil, or "closest value," reach for a guided walk: compare to the key at each node and descend into exactly one child while tracking the best candidate. You almost never need to visit both subtrees.
Mind the comparison direction. Ceil uses >= and goes left on a match; floor (largest value <= key)
flips it to <= and goes right on a match. Swapping them silently returns the wrong neighbor.
Practice
For this tree, what is the ceil of key = 7?
1. What is the ceil of a key in a BST?
2. When node.val >= key, which way do we move?
3. Why is this O(h) and not O(n)?
4. What does the function return if every value in the tree is smaller than the key?