Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Data Structures & Algorithms (DSA)  Module 9 – Trees and binary search trees

Ordered maps: floor, ceiling and rank queries

Floor, ceiling and range queries in O(log n) with TreeMap, std::map and sorted lists, and the subtree sizes that answer rank and k-th smallest.

  • Advanced
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free core, Pro depth: 11 of 30 minutes free
Free: What a hash map cannot answer · Floor and ceiling in one walk down the tree · The same queries in Java, C++ and Python · 3 more sections. With a pass: 3 in-depth parts and 3 interview answers, about 19 minutes.

What you will learn

  • Use TreeMap, std::map and sorted lists for floor, ceiling and range queries
  • Extend a binary search tree with subtree sizes to answer rank and k-th smallest
  • Choose an ordered structure in languages that lack one, by size and update rate

Before you start

Ordered maps keep their keys sorted, so besides “is this key here?” they answer questions about neighbours: the largest key at or below x, its floor; the smallest key at or above x, its ceiling; the next and previous keys; and every key in a range. With one extra number in each node, the size of its subtree, they also answer rank queries, how many keys are smaller than x, and find the k-th smallest key. A balanced search tree answers each of these in O(log n), plus the size of the output for a range. Java has TreeMap and C++ has std::map; Python, JavaScript and Go have no ordered map in their standard libraries, so programs there keep a sorted list and binary search it, which is fast to query and slow to change.

What a hash map cannot answer

A hash map finds a key in O(1) on average, but it scatters keys on purpose, so it cannot tell which key comes next. Many everyday questions are about neighbours rather than exact keys: the free appointment closest to the one a patient asked for, the next train after a given time, the highest bid at or below a price, the address range that contains an address. Ordered maps answer all of them with two primitives, floor and ceiling.

Floor and ceiling in one walk down the tree

To find the floor of x, start at the root. A key at or below x is a candidate, and a better one can only lie to its right, so remember it and go right; a key above x cannot be the floor, so go left. When the walk falls off the tree, the last candidate is the floor. The ceiling is the mirror image. Both follow one path from the root, so they cost O(h): O(log n) in a balanced tree.

A search tree with root 120. The walk for 130 goes right to 180, then left to 135: floor 120, ceiling 135.120: below 130, floor so far60180: above 130, ceiling so far3075135: above 130, the ceiling24015The walk ends below 135: floor(130) = 120, ceiling(130) = 135, after 3 nodes130 > 120: go right130 < 180: go left

Floor and ceiling of 130 come from one walk down the tree

Text description of the diagram

The diagram shows the search tree of the lesson's run, with root 120, and the walk that finds the floor and the ceiling of 130.

  1. The root 120 is below 130, so it is the best floor so far, and anything closer must be to its right: the walk goes right.
  2. The next node, 180, is above 130, so it is the best ceiling so far, and anything closer must be to its left: the walk goes left.
  3. The node 135 is above 130 and closer than 180, so it becomes the ceiling. It has no left child, so the walk ends after three nodes.
  4. The answer is floor(130) = 120 and ceiling(130) = 135. The other nodes of the tree, 60, 30, 15, 75 and 240, are never visited.
Floor and ceiling in a search tree, with the nodes visited Python · bst/floor_ceiling.py
"""Floor and ceiling in a binary search tree: one walk from the root, remembering the best candidate so far."""


class Node:
    __slots__ = ("key", "left", "right")

    def __init__(self, key):
        self.key, self.left, self.right = key, None, None


def insert(root, key):
    if root is None:
        return Node(key)
    if key < root.key:
        root.left = insert(root.left, key)
    elif key > root.key:
        root.right = insert(root.right, key)
    return root


def floor(root, x, path):
    """The largest key <= x: a key <= x is a candidate, and anything better can only be to its right."""
    best, node = None, root
    while node:
        path.append(node.key)
        if node.key == x:
            return x
        if node.key < x:
            best, node = node.key, node.right
        else:
            node = node.left
    return best


def ceiling(root, x, path):
    """The smallest key >= x: the mirror image."""
    best, node = None, root
    while node:
        path.append(node.key)
        if node.key == x:
            return x
        if node.key > x:
            best, node = node.key, node.left
        else:
            node = node.right
    return best


root = None
for key in (120, 60, 180, 30, 75, 135, 240, 15):  # the free slots, inserted in a balanced order
    root = insert(root, key)
for x in (40, 130, 250, 5):
    fp, cp = [], []
    f, c = floor(root, x, fp), ceiling(root, x, cp)
    print(f"x = {x:3}: floor {f}, path {fp}; ceiling {c}, path {cp}")

Output

x =  40: floor 30, path [120, 60, 30]; ceiling 60, path [120, 60, 30]
x = 130: floor 120, path [120, 180, 135]; ceiling 135, path [120, 180, 135]
x = 250: floor 240, path [120, 180, 240]; ceiling None, path [120, 180, 240]
x =   5: floor None, path [120, 60, 30, 15]; ceiling 15, path [120, 60, 30, 15]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 floor_ceiling.py

Each query visited three or four of the eight nodes. Key 250 has no ceiling and 5 has no floor, so every API needs a way to say “none”.

The same queries in Java, C++ and Python

Question Java TreeMap C++ std::map Python, a sorted list a
ceiling, the smallest key at or above x ceilingKey(x) lower_bound(x) a[bisect_left(a, x)]
floor, the largest key at or below x floorKey(x) prev(upper_bound(x)) a[bisect_right(a, x) - 1]
keys from lo to hi, both included subMap(lo, true, hi, true) lower_bound(lo) to upper_bound(hi) a[bisect_left(a, lo):bisect_right(a, hi)]
when there is no such key null end(), or no key before begin() a position outside the list

The run below keeps a clinic’s free appointment slots in a sorted Python list:

The nearest free appointment, with bisect Python · slots/slots.py
"""Free appointment slots in a sorted list: floor, ceiling and a range, with bisect."""

from bisect import bisect_left, bisect_right, insort

free = [15, 30, 60, 75, 120, 135, 180, 240]  # minutes after 09:00 that are still free


def clock(minutes):
    return f"{9 + minutes // 60:02}:{minutes % 60:02}"


def ceiling(a, x):
    """The smallest value >= x, or None."""
    i = bisect_left(a, x)  # the first position whose value is >= x
    return a[i] if i < len(a) else None


def floor(a, x):
    """The largest value <= x, or None."""
    i = bisect_right(a, x)  # the first position whose value is > x
    return a[i - 1] if i > 0 else None


def show(minutes):
    return clock(minutes) if minutes is not None else "none"


for wanted in (40, 60, 250, 5):
    print(f"wants {clock(wanted)}: at or after {show(ceiling(free, wanted))}, at or before {show(floor(free, wanted))}")

lo, hi = bisect_left(free, 60), bisect_right(free, 180)  # every free slot from 10:00 to 12:00, both included
print("free from 10:00 to 12:00:", ", ".join(clock(m) for m in free[lo:hi]))

del free[bisect_left(free, 60)]  # 10:00 is booked
insort(free, 45)  # a cancellation frees 09:45
print("after booking 10:00 and freeing 09:45, a patient who wants 10:00 gets", show(ceiling(free, 60)),
      "or", show(floor(free, 60)))

Output

wants 09:40: at or after 10:00, at or before 09:30
wants 10:00: at or after 10:00, at or before 10:00
wants 13:10: at or after none, at or before 13:00
wants 09:05: at or after 09:15, at or before none
free from 10:00 to 12:00: 10:00, 10:15, 11:00, 11:15, 12:00
after booking 10:00 and freeing 09:45, a patient who wants 10:00 gets 10:15 or 09:45

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 slots.py

A patient who wants 09:40 is offered 10:00 or 09:30; 10:00 is its own floor and ceiling; nothing is free after 13:10 or before 09:05. Booking deletes from the list and a cancellation inserts into it, both O(n).

Rank queries and the k-th smallest, from subtree sizes

Store in every node the size of its subtree. For rank(x), walk down from the root: whenever the walk goes right past a node, that node and its whole left subtree are smaller than x, so add size(left) + 1. For select(k), the k-th smallest counted from 0, compare k with the size of the left subtree: smaller means go left, equal means this node, larger means subtract size(left) + 1 and go right. Both walk one path. An insertion adds 1 to the size of every node it passes, and a deletion subtracts 1.

Rank and select on 100,000 shuffled scores, checked against bisect Python · rank/order_stats.py
"""Rank and k-th smallest from subtree sizes, checked against bisect on 100,000 shuffled scores."""

import random
from bisect import bisect_left


class Node:
    __slots__ = ("key", "left", "right", "size")

    def __init__(self, key):
        self.key, self.left, self.right, self.size = key, None, None, 1


def size(node):
    return node.size if node else 0


def insert(root, key):
    """Ordinary BST insertion that also adds 1 to the size of every node it passes (keys are distinct)."""
    if root is None:
        return Node(key)
    node = root
    while True:
        node.size += 1
        side = "left" if key < node.key else "right"
        child = getattr(node, side)
        if child is None:
            setattr(node, side, Node(key))
            return root
        node = child


def rank(root, x):
    """How many keys are smaller than x: every time the walk goes right, the left subtree and the node count."""
    smaller, node, visited = 0, root, 0
    while node:
        visited += 1
        if x <= node.key:
            node = node.left
        else:
            smaller += size(node.left) + 1
            node = node.right
    return smaller, visited


def select(root, k):
    """The k-th smallest key, counting from 0: compare k with the size of the left subtree at each node."""
    node, visited = root, 0
    while node:
        visited += 1
        left = size(node.left)
        if k < left:
            node = node.left
        elif k == left:
            return node.key, visited
        else:
            k -= left + 1
            node = node.right
    raise IndexError("k is out of range")


rng = random.Random(8)
scores = rng.sample(range(10_000_000), 100_000)  # distinct scores, in random order
root = None
for s in scores:
    root = insert(root, s)
ordered = sorted(scores)

print(f"rank of 5,000,000: {rank(root, 5_000_000)[0]:,} keys are smaller; bisect says {bisect_left(ordered, 5_000_000):,}")
print("select k = 0, 49,999 and 99,999:", ", ".join(f"{select(root, k)[0]:,}" for k in (0, 49_999, 99_999)))
print("               bisect says     ", ", ".join(f"{ordered[k]:,}" for k in (0, 49_999, 99_999)))

worst = 0
for _ in range(10_000):  # random checks against the sorted list
    x = rng.randrange(10_000_000)
    r, v1 = rank(root, x)
    k = rng.randrange(100_000)
    key, v2 = select(root, k)
    assert r == bisect_left(ordered, x) and key == ordered[k]
    worst = max(worst, v1, v2)
print(f"10,000 random rank and select queries agree with bisect; the longest walk visited {worst} nodes")

Output

rank of 5,000,000: 49,973 keys are smaller; bisect says 49,973
select k = 0, 49,999 and 99,999: 98, 5,002,295, 9,999,793
               bisect says      98, 5,002,295, 9,999,793
10,000 random rank and select queries agree with bisect; the longest walk visited 37 nodes

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 order_stats.py

All 10,000 random rank queries and select queries agreed with a sorted list. This plain tree is as tall as its random insertion order made it, so its longest walk visited 37 nodes; a balanced tree keeps every walk near log₂ n, about 17 here.

Sorted lists, flat maps and languages without an ordered map

Python’s bisect finds a position in O(log n), but its documentation warns that insort is dominated by the O(n) insertion step: every larger element shifts by one. C++23’s std::flat_map makes the same trade on purpose, with two sorted arrays, logarithmic search and linear insertion, for small or read-mostly maps. Go and JavaScript offer neither: Go’s map iteration order is not specified, and a JavaScript Map iterates in insertion order. For changing data there, the usual choices are a sorted array with binary search, a Fenwick tree over keys known in advance, or a randomised search tree such as the treap of the next lesson.

Exercise

Exercise · Medium · Python, JavaScript

A seat map that finds the nearest free seat

A hall has seats numbered 0 to n − 1, all free at the start. Write the class Seats(n) with three methods:

  • book(seat): takes the seat and returns True, or returns False if it was already taken (JavaScript: true and false);
  • release(seat): makes the seat free again; releasing a free seat changes nothing;
  • nearest_free(x) (JavaScript: nearestFree(x)): the free seat closest to position x, which may be any integer, even outside 0 to n − 1. On a tie, return the smaller seat number. If every seat is taken, return None (JavaScript: null).

Finding the nearest free seat may not scan the seats one by one: the tests ask 4,000 questions of a hall of 20,000 seats, and compare your class with a slow, obviously correct one on 20,000 random operations.

Python · Starter code · seats.py

class Seats:
    """Seats numbered 0 to n - 1, all free at the start."""

    def __init__(self, n):
        raise NotImplementedError("write Seats")

    def book(self, seat):
        """Takes the seat; returns False if it was already taken."""

    def release(self, seat):
        """Makes the seat free again (releasing a free seat changes nothing)."""

    def nearest_free(self, x):
        """The free seat closest to x, the smaller one on a tie; None if every seat is taken."""
The sample tests · test_seats.py
import random

from seats import Seats


class SlowSeats:
    """Obviously correct and slow: a set of taken seats, and a scan for the nearest free one."""

    def __init__(self, n):
        self.n, self.taken = n, set()

    def book(self, seat):
        if seat in self.taken:
            return False
        self.taken.add(seat)
        return True

    def release(self, seat):
        self.taken.discard(seat)

    def nearest_free(self, x):
        free = [s for s in range(self.n) if s not in self.taken]
        return min(free, key=lambda s: (abs(s - x), s)) if free else None


def test_small_hall():
    """booking, releasing and the nearest free seat in a hall of 6 seats"""
    s = Seats(6)
    assert s.nearest_free(3) == 3
    assert s.book(3) is True and s.book(3) is False
    assert s.nearest_free(3) == 2  # 2 and 4 are both one seat away: the smaller wins
    s.book(2)
    assert s.nearest_free(3) == 4
    s.release(3)
    assert s.nearest_free(3) == 3


def test_outside_and_full():
    """positions outside the hall find the nearest end; a full hall answers None"""
    s = Seats(3)
    assert s.nearest_free(-7) == 0 and s.nearest_free(99) == 2
    for seat in range(3):
        s.book(seat)
    assert s.nearest_free(1) is None
    s.release(1)
    s.release(1)
    assert s.nearest_free(-7) == 1


def test_against_slow_version():
    """20,000 random operations in a hall of 300 seats give the same answers as the slow version"""
    rng = random.Random(9)
    fast, slow = Seats(300), SlowSeats(300)
    for _ in range(20_000):
        op, seat = rng.random(), rng.randrange(-20, 320)
        if op < 0.45:
            if 0 <= seat < 300:
                assert fast.book(seat) == slow.book(seat)
        elif op < 0.7:
            if 0 <= seat < 300:
                fast.release(seat)
                slow.release(seat)
        else:
            assert fast.nearest_free(seat) == slow.nearest_free(seat), f"nearest_free({seat})"


def test_large_hall():
    """a hall of 20,000 seats with every even seat booked answers 4,000 queries without scanning"""
    s = Seats(20_000)
    for seat in range(0, 20_000, 2):
        s.book(seat)
    assert s.nearest_free(10_000) == 9_999  # 9,999 and 10,001 are both free and one away
    assert all(s.nearest_free(x) % 2 == 1 for x in range(0, 20_000, 5))

JavaScript · Starter code · seats.mjs

/** Seats numbered 0 to n - 1, all free at the start. */
export class Seats {
  constructor(n) {
    throw new Error('write Seats');
  }

  /** Takes the seat; returns false if it was already taken. */
  book(seat) {}

  /** Makes the seat free again (releasing a free seat changes nothing). */
  release(seat) {}

  /** The free seat closest to x, the smaller one on a tie; null if every seat is taken. */
  nearestFree(x) {}
}
The sample tests · seats.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { Seats } from './seats.mjs';

class SlowSeats {
  constructor(n) {
    this.n = n;
    this.taken = new Set();
  }
  book(seat) {
    if (this.taken.has(seat)) return false;
    this.taken.add(seat);
    return true;
  }
  release(seat) {
    this.taken.delete(seat);
  }
  nearestFree(x) {
    let best = null;
    for (let s = 0; s < this.n; s++) {
      if (this.taken.has(s)) continue;
      if (best === null || Math.abs(s - x) < Math.abs(best - x)) best = s;
    }
    return best;
  }
}

test('booking, releasing and the nearest free seat in a hall of 6 seats', () => {
  const s = new Seats(6);
  assert.equal(s.nearestFree(3), 3);
  assert.equal(s.book(3), true);
  assert.equal(s.book(3), false);
  assert.equal(s.nearestFree(3), 2);
  s.book(2);
  assert.equal(s.nearestFree(3), 4);
  s.release(3);
  assert.equal(s.nearestFree(3), 3);
});

test('positions outside the hall find the nearest end; a full hall answers null', () => {
  const s = new Seats(3);
  assert.equal(s.nearestFree(-7), 0);
  assert.equal(s.nearestFree(99), 2);
  for (let seat = 0; seat < 3; seat++) s.book(seat);
  assert.equal(s.nearestFree(1), null);
  s.release(1);
  s.release(1);
  assert.equal(s.nearestFree(-7), 1);
});

test('20,000 random operations in a hall of 300 seats give the same answers as the slow version', () => {
  let seed = 9;
  const next = (n) => {
    seed = (seed * 1103515245 + 12345) % 2147483648;
    return seed % n;
  };
  const fast = new Seats(300);
  const slow = new SlowSeats(300);
  for (let i = 0; i < 20000; i++) {
    const op = next(100);
    const seat = next(340) - 20;
    if (op < 45) {
      if (seat >= 0 && seat < 300) assert.equal(fast.book(seat), slow.book(seat));
    } else if (op < 70) {
      if (seat >= 0 && seat < 300) {
        fast.release(seat);
        slow.release(seat);
      }
    } else assert.equal(fast.nearestFree(seat), slow.nearestFree(seat), `nearestFree(${seat})`);
  }
});

test('a hall of 20,000 seats with every even seat booked answers 4,000 queries without scanning', () => {
  const s = new Seats(20000);
  for (let seat = 0; seat < 20000; seat += 2) s.book(seat);
  assert.equal(s.nearestFree(10000), 9999);
  for (let x = 0; x < 20000; x += 5) assert.equal(s.nearestFree(x) % 2, 1);
});
A hint

Keep the free seats in a sorted list. The nearest free seat to x is either the smallest free seat at or above x (a ceiling, from bisect_left) or the largest free seat below it (the one just before that position): compare the two distances, and prefer the smaller seat when they are equal.

The sample tests run on this device, in your browser (Pyodide, QuickJS): nothing is sent to mysmartcopilot.com. The first run of each language downloads it: Python (about 13.5 MB) or JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

4 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 4 The free slots are 09:15, 09:30, 10:00 and 10:15. What are the floor and the ceiling of 10:00?

    Choose one answer.

    Show the answer to question 1

    Answer: Both are 10:00

    The floor is the largest value at or below the target and the ceiling the smallest value at or above it, so a value that is present is both. The recorded run shows the same for "wants 10:00".

  2. Question 2 of 4 In a sorted Python list a, which expression gives the position of the floor of x, when x has a floor?

    Choose one answer.

    Show the answer to question 2

    Answer: bisect_right(a, x) - 1

    bisect_right(a, x) is the first position whose value is greater than x, so the position just before it holds the largest value at or below x. bisect_left(a, x) - 1 is the last value strictly below x, which is wrong when x itself is in the list.

  3. Question 3 of 4 A walk for rank(x) goes right at two nodes whose left subtrees hold 4 and 10 keys, and left everywhere else. How many keys are smaller than x?

    Type a number.

    Show the answer to question 3

    Answer: 16

    Each time the walk goes right, the node and its whole left subtree are smaller than x: (4 + 1) + (10 + 1) = 16. Going left adds nothing.

  4. Question 4 of 4 Which of these does the Python standard library offer for floor and ceiling queries on changing data?

    Choose one answer.

    Show the answer to question 4

    Answer: The bisect module on a sorted list, with O(n) insertions

    Python has no balanced search tree or sorted map in its standard library. bisect searches a sorted list in O(log n), but its documentation notes that insort is dominated by the O(n) insertion step.

Floor, ceiling and ranges in five languages, and the off-by-one traps Pro: opens with a Pro, Premium or Ultimate pass

Every library names floor and ceiling differently, and most bugs in ordered queries are one position off. This part writes the clinic in JavaScript, Java, C++ and Go, then reproduces four traps with their fixes.

In this part:

  • The clinic in JavaScript, Java, C++ and Go
  • Four off-by-one traps, reproduced
  • What a range query costs
  • Edge cases worth a test each

5 runnable examples (C++, Go, Java, JavaScript, Python) · about 3 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Rank and select kept exact through rotations, proved and measured Pro: opens with a Pro, Premium or Ultimate pass

Subtree sizes are useful only if every update keeps them exact, including the rotations of a balanced tree. This part proves rank and select correct, adds sizes to the AVL tree, and measures where a sorted list stops being enough.

In this part:

  • Why rank and select return the right answer: the invariant
  • Sizes through rotations: recompute two nodes, the lower one first
  • Measured: when a sorted list stops being enough

2 runnable examples (Python) · about 2 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Leaderboards, bookings and choosing a structure, with numbers Pro: opens with a Pro, Premium or Ultimate pass

Which ordered structure is right depends on whether every key is known in advance, how often keys change, and which questions are asked. This part builds a leaderboard on a Fenwick tree, checks bookings for overlap with two lookups, and ends with a decision table.

In this part:

  • A leaderboard with rank and k-th from a Fenwick tree
  • Bookings that must not overlap: two neighbours are enough, proved
  • Choosing an ordered structure: a trade-off table

2 runnable examples (Python) · about 3 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Interview questions

  1. Fresher · Warm-up

    What do floor and ceiling return in an ordered map, and what happens when no such key exists?

    The floor of x is the largest key at or below x, and the ceiling is the smallest key at or above x; a key equal to x is both. In a balanced search tree each takes one walk from the root, O(log n). When there is no such key, for a floor of a value below every key or a ceiling above every key, each library says so in its own way: Java's TreeMap.floorKey and ceilingKey return null, C++'s lower_bound returns end() (and a floor needs a check against begin() before stepping back), and with Python's bisect on a sorted list the computed position falls outside the list, which the code must test before indexing.

  2. Mid to senior

    A game leaderboard holds 2 million scores that change 5,000 times a second, and any player can ask for their rank. Which structure would you use, what does each operation cost, and what changes when several players share a score?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Fresher to mid

    Meeting-room bookings are half-open intervals [start, end) that never overlap. Using an ordered map keyed by start, check a new booking for overlaps in O(log n). Which stored bookings must you look at, and why are they enough?

    Model answer, Pro. First step and follow-ups

  4. Mid to senior

    A Python service keeps 5 million order prices in a list sorted with bisect.insort and receives 2,000 new prices a second, plus cancellations. Lookups are fast, but the service falls behind. Estimate why, and what would you replace the list with?

    Model answer, Pro. First step and follow-ups

Key takeaways

  • Floor and ceiling find the neighbours of any value with one walk down a search tree, in O(log n) when it is balanced.
  • TreeMap and std::map answer them directly; in Python, bisect on a sorted list does, with O(n) updates.
  • Storing subtree sizes turns the same walk into rank queries and k-th smallest lookups.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.