Trees · Binary Search Tree Operations

BST Floor and Ceil

Walking down from the root tracking the best candidate seen so far on the correct side - the largest value not exceeding the key for floor, the smallest value not below it for ceil - narrowing toward the key without ever needing to explore both children at a node.

  • 7 min read
  • 9 practice questions
  • 4 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: both bounds at once
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Plain search answers "is this key here?" Floor and ceil answer the question you usually have when it isn't: what's the nearest value on the right side of my limit? The floor of a key is the largest value at or below it. The ceil is the smallest value at or above it. Both lie on the path a search for the key follows, so they cost no more than the search. Range queries, with a bound on each side, come from the same idea.

We'll go from simple to more technical:

  1. The pattern: walk down and record a candidate on the safe side.
  2. A second flavor: range queries with both bounds, pruning whole subtrees.
  3. Where nearest-match queries run in production (time series, prices, versions).
  4. What breaks it: stopping early, the wrong side, sentinels, and no pruning.

The approach

Walk down from the root as if you were searching for the key. For the floor, every node smaller than the key is a candidate. Record it, then go right to look for a closer one. A node larger than the key can't be the floor, so go left without recording. The last candidate you recorded is the answer.

Find the floor of 55 in this tree:

40

20

60

10

30

50

70

40

20

60

10

30

50

70

NodeCompared with 55Floor so farNext step
40smaller40right
60larger40left
50smaller50right
(none)50stop: the floor is 50

Java

int floorBST(TreeNode root, int key) {
    int floor = -1;
    TreeNode cur = root;
    while (cur != null) {
        if (cur.val == key) return cur.val;
        if (cur.val < key) { floor = cur.val; cur = cur.right; }   // <= key: a candidate - look for a closer one
        else cur = cur.left;
    }
    return floor;
}

Python

def floor_bst(root: TreeNode | None, key: int) -> int:
    floor = -1
    cur = root
    while cur is not None:
        if cur.val == key:
            return cur.val
        if cur.val < key:
            floor = cur.val              # <= key: a candidate - look for a closer one
            cur = cur.right
        else:
            cur = cur.left
    return floor

Ceil is the mirror image. Record a node when it's larger than the key and go left; go right without recording when it's smaller. For 55 the ceil walk records 60 and ends with 60. Recording a candidate never ends the walk, since a closer value can sit further down. The walk ends on an exact match or when it falls off the tree. If nothing was recorded, the answer is -1: every value is on the wrong side of the key.

Floor and ceil lie on the same path, so one walk can track both. The value closest to a key is always one of the two.

A second flavor: both bounds at once

Floor and ceil each use one bound. A range query, such as "sum every value in [lo, hi]", uses both. The BST order lets it skip whole subtrees:

Java

long rangeSum(TreeNode node, int lo, int hi) {
    if (node == null) return 0;
    if (node.val < lo) return rangeSum(node.right, lo, hi);   // node and its left subtree are all too small
    if (node.val > hi) return rangeSum(node.left, lo, hi);    // node and its right subtree are all too large
    return node.val + rangeSum(node.left, lo, hi) + rangeSum(node.right, lo, hi);
}

Python

def range_sum(node: TreeNode | None, lo: int, hi: int) -> int:
    if node is None:
        return 0
    if node.val < lo:
        return range_sum(node.right, lo, hi)   # node and its left subtree are all too small
    if node.val > hi:
        return range_sum(node.left, lo, hi)    # node and its right subtree are all too large
    return node.val + range_sum(node.left, lo, hi) + range_sum(node.right, lo, hi)

A node below lo rules out its whole left subtree. A node above hi rules out its whole right subtree. Only nodes inside the range explore both sides. So the walk visits the m values in range plus O(h) nodes along the edges. On a balanced tree of 65,535 nodes, a range holding 11 values touched 28 nodes, not 65,535.

Where you'll see it

Floor and ceil are the "nearest match" queries of every ordered index. Think of the latest event at or before a timestamp, or the price tier that applies to an order total. Java's TreeMap.floorKey and ceilingKey are exactly these walks. A database range scan (WHERE t BETWEEN lo AND hi) starts with a ceil lookup for lo and stops once it passes hi.

Which tool for which job?

You needUse
Largest value at or below the keyfloor: record on right steps
Smallest value at or above the keyceil: record on left steps
Strictly below or abovepredecessor / successor: keep walking on a match
The value closest to the keytrack both in one walk; compare differences in long
Everything in [lo, hi]a range walk that prunes subtrees outside the bounds
Java / PythonfloorKey / ceilingKey; bisect_right(s, k) - 1 / bisect_left(s, k)

Common mistakes

MistakeWhat happensDo this instead
Returning the first candidatemisses a closer value further down (40 instead of 50 above)keep walking until a match or the end
Recording a floor on a left steprecords a value above the keyfloor records on right steps only
-1 as "none" with negative valuesa real answer and "none" look the samenull / None or a found flag
A range query with no pruningO(n) even for a tiny rangeskip the side that's out of range
int differences for "closest"the difference wraps and a far value winscompute in long

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. In [8, 5, 10, 3, 6, null, 12], what are the floor and ceil of 7, and which nodes does the floor walk record?

Show answer

Floor 6, ceil 8. The floor walk goes left at 8 without recording, since 8 is too big. It records 5 and goes right, records 6 and goes right, then falls off. The ceil walk records 8 at the root and goes left. 5 and 6 are too small, so 8 stands.

2. Why can the closest value to a key always be found in one O(h) walk?

Show answer

The closest value is either the floor or the ceil. Nothing else can be closer. Both are recorded along the search path for the key, so one walk that tracks the best difference finds it.

3. A range sum over [lo, hi] reaches a node below lo. What can it skip, and why?

Show answer

The node and its whole left subtree. Everything there is smaller than the node, so it's smaller than lo too. Only the right subtree can still hold values in range. This pruning makes a range query O(h + m) instead of O(n).

Key takeaways

  • The floor is the largest value at or below the key, and the ceil the smallest at or above. Both cost O(h).
  • Record a candidate on a step in the safe direction (right for floor, left for ceil) and keep walking.
  • The closest value to a key is its floor or its ceil. Compare differences in long.
  • A range query uses both bounds to skip whole subtrees: O(h + m) for m values in range.
  • The strict versions are the predecessor and successor. On an exact match they keep walking.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
At or belowfloor (record on right steps)
At or aboveceil (record on left steps)
Strictly below or abovepredecessor / successor
Closestfloor or ceil, differences in long
Everything in [lo, hi]range walk with pruning
Count in a range, fastsubtree sizes, two rank queries

Snippets worth knowing by heart

Java

Integer floor = null;
for (TreeNode cur = root; cur != null; ) {
    if (cur.val == key) return cur.val;
    if (cur.val < key) { floor = cur.val; cur = cur.right; }
    else cur = cur.left;
}

Python

i = bisect.bisect_right(s, k)
floor = s[i - 1] if i > 0 else None
j = bisect.bisect_left(s, k)
ceil = s[j] if j < len(s) else None

The traps

  • Stopping at the first candidate: a closer value further down is missed. Keep walking until a match or null.
  • Recording on the wrong step: a floor recorded on a left step is above the key. Record floors on right steps only.
  • A -1 sentinel with negative values: a real answer and "none" look the same. Return null, or a found flag.
  • No pruning on range queries: a tiny range still costs O(n). Skip the side that's out of range.
  • bisect_left for a floor: bisect_left(a, x) - 1 is the strict predecessor, so an exact match is missed. Use bisect_right(a, x) - 1.

Staff-level points, one line each

  • Balanced 65,535 keys: a range of 11 values visited 24 nodes and a range of 1,001 visited 1,016. On a 10,000-key chain, a range of 10 visited all 10,000.
  • Java int: Math.abs(-2000000000 - 2000000000) gave 294967296, so the far value won. Use long.
  • Math.abs(Integer.MIN_VALUE) is -2147483648.
  • TreeMap<Double>: NaN sorts last, and ceilingKey(5.0) returned NaN. Python's sorted with a NaN didn't sort.
  • Effective-dated values are a floorEntry, and the result before the first version is null.

Say it in an interview

  • "Floor and ceil are on the search path, so they're O(h)."
  • "I record a candidate when I step in the safe direction and keep walking."
  • "A range query prunes subtrees: O(h + m), as long as the tree is balanced."
  • "For closest, I compute differences in a long and state the tie rule."

Practice

This skill comes with 9 multiple-choice questions that tell you which misconception a wrong answer reveals and 4 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice BST Floor and Ceil free

Floor in a BST

EasyFreeCoding problemJava · Python

Given the root of a binary search tree and a key, find the floor of the key: the largest node value that is less than or equal to the key. If no such value exists, return -1.

Ceil in a BST

EasyFreeCoding problemJava · Python

Given the root of a binary search tree and a key, find the ceil of the key: the smallest node value that is greater than or equal to the key. If no such value exists, return -1.

Closest Value in a BST

MediumProCoding problemJava · Python

Given the root of a binary search tree (all values distinct) and a target, return the node value closest to target. If two values are equally close, return the smaller one.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice BST Floor and Ceil free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime