LLD
Machine Coding
Interview Course
Java โ€ข Interview Prep
โšก Algorithms & Data Structures/

Interval Trees & Overlap Detection

Lesson 4 of 5

Interval Trees & Overlap Detection


What is it? (The Analogy)

Imagine you are the school receptionist managing the meeting room schedule. Teachers

book time slots: Ms. Smith wants 9:00-10:30, Mr. Jones wants 10:00-11:00, Mrs. Lee wants

11:00-12:00. When a new booking request comes in, you need to instantly answer: "Does this

overlap with any existing booking?"

If you only have 5 bookings, you can check each one. But what if you have 10,000 bookings

across 50 rooms for the entire semester? Checking every single booking for every new request

would be incredibly slow.

An Interval Tree is like a super-organized filing cabinet for time ranges (or any ranges

of numbers). Instead of searching through every booking, you organize them in a smart tree

structure so that finding all overlapping bookings takes O(log N + K) time, where K is the

number of overlaps found. It is like having a librarian who can instantly tell you which books

cover a specific topic range, without scanning every shelf.

Core idea: An Interval Tree is a balanced binary search tree where each node stores an

interval, and the tree is organized so you can efficiently skip entire branches that

cannot possibly overlap with your query.


Why do we need it?

The Problem: Brute Force Overlap Checking

java
1// BRUTE FORCE: Check every existing interval for overlap โ€” O(N) per query
2boolean hasOverlap(List<int[]> bookings, int start, int end) {
3    for (int[] booking : bookings) {
4        // Two intervals [a,b] and [c,d] overlap if a < d AND c < b
5        if (booking[0] < end && start < booking[1]) {
6            return true;  // Overlap found!
7        }
8    }
9    return false;
10}

Cost: O(N) per query. If you have N existing bookings and Q new requests, total cost

is O(N * Q). With 10,000 bookings and 5,000 queries, that is 50 million comparisons.

Simpler Alternative: Sorted List + Binary Search

Before jumping to Interval Trees, there is a simpler approach that works well when intervals

do not overlap (like a single room's calendar):

java
// If intervals are non-overlapping and sorted, use binary search โ€” O(log N)
// But this ONLY works when existing intervals don't overlap each other!

The Full Solution: Interval Trees

When intervals CAN overlap (like bookings across multiple rooms, or overlapping events),

you need an Interval Tree. It handles all cases efficiently.


How it works โ€” Step by Step

An Interval Tree is built on top of a BST (Binary Search Tree) with these modifications:

  1. Each node stores an interval [low, high].
  2. **The BST is ordered by the low value** of each interval (the start time).
  3. **Each node also stores the maximum high value** in its entire subtree. This is the

secret sauce โ€” it lets us prune branches during search.

  1. **To search for overlaps with a query interval [qLow, qHigh]:**

- At each node, check if the node's interval overlaps the query.

- If the left child's max is >= qLow, the left subtree MIGHT have overlaps โ€” go left.

- Otherwise, go right (the left subtree definitely has no overlaps).

  1. To insert a new interval: Insert like a normal BST (by low value), then update

the max values going back up.

The overlap condition: Two intervals [a, b] and [c, d] overlap if and only if

a < d AND c < b. Equivalently, they do NOT overlap if a >= d OR c >= b.


Let's Build It Together

Let's build a Restaurant Reservation System where customers book time slots and we need

to detect conflicts.

java
1/**
2 * Interval Tree for detecting overlapping reservations.
3 *
4 * Scenario: "Pasta Palace" restaurant takes reservations.
5 * Each reservation is a time interval [startHour, endHour].
6 * We need to quickly find if a new reservation conflicts with existing ones.
7 */
8public class IntervalTree {
9
10    /**
11     * Each node in the tree represents one reservation.
12     */
13    static class Node {
14        int low;        // Start time of this reservation
15        int high;       // End time of this reservation
16        int maxHigh;    // Maximum end time in this entire subtree
17        Node left;      // Left child (reservations starting earlier)
18        Node right;     // Right child (reservations starting later)
19        String guest;   // Who made this reservation (for fun!)
20
21        Node(int low, int high, String guest) {
22            this.low = low;
23            this.high = high;
24            this.maxHigh = high;  // Initially, max is just this node's high
25            this.guest = guest;
26            this.left = null;
27            this.right = null;
28        }
29    }
30
31    private Node root;
32
33    public IntervalTree() {
34        this.root = null;
35    }
36
37    /**
38     * Insert a new reservation into the tree.
39     *
40     * We insert by 'low' value (start time), just like a regular BST.
41     * After inserting, we update maxHigh values going back up.
42     */
43    public void insert(int low, int high, String guest) {
44        root = insertHelper(root, low, high, guest);
45    }
46
47    private Node insertHelper(Node node, int low, int high, String guest) {
48        // Base case: found an empty spot, create the node
49        if (node == null) {
50            return new Node(low, high, guest);
51        }
52
53        // BST insert: go left if new interval starts earlier, right otherwise
54        if (low < node.low) {
55            node.left = insertHelper(node.left, low, high, guest);
56        } else {
57            node.right = insertHelper(node.right, low, high, guest);
58        }
59
60        // KEY STEP: Update the maxHigh for this subtree
61        // The max could be from this node, or from any descendant
62        node.maxHigh = Math.max(node.maxHigh, high);
63
64        return node;
65    }
66
67    /**
68     * Check if a proposed reservation overlaps with ANY existing one.
69     *
70     * This is the star of the show โ€” O(log N) average case!
71     */
72    public Node findOverlap(int low, int high) {
73        return findOverlapHelper(root, low, high);
74    }
75
76    private Node findOverlapHelper(Node node, int low, int high) {
77        // Base case: reached a null node, no overlap found
78        if (node == null) {
79            return null;
80        }
81
82        // Check if THIS node's interval overlaps with [low, high]
83        // Overlap condition: node.low < high AND low < node.high
84        if (node.low < high && low < node.high) {
85            return node;  // Found an overlap!
86        }
87
88        // Decide which subtree to explore:
89        // If the left child exists AND its maxHigh > low,
90        // then there MIGHT be an overlap in the left subtree
91        if (node.left != null && node.left.maxHigh > low) {
92            return findOverlapHelper(node.left, low, high);
93        }
94
95        // Otherwise, try the right subtree
96        return findOverlapHelper(node.right, low, high);
97    }
98
99    /**
100     * Find ALL overlapping reservations (not just the first one).
101     */
102    public List<Node> findAllOverlaps(int low, int high) {
103        List<Node> results = new ArrayList<>();
104        findAllOverlapsHelper(root, low, high, results);
105        return results;
106    }
107
108    private void findAllOverlapsHelper(Node node, int low, int high,
109                                        List<Node> results) {
110        if (node == null) return;
111
112        // Check this node
113        if (node.low < high && low < node.high) {
114            results.add(node);
115        }
116
117        // Explore left subtree if it might contain overlaps
118        if (node.left != null && node.left.maxHigh > low) {
119            findAllOverlapsHelper(node.left, low, high, results);
120        }
121
122        // Explore right subtree if it might contain overlaps
123        if (node.right != null && node.low < high) {
124            findAllOverlapsHelper(node.right, low, high, results);
125        }
126    }
127}

Using it:

java
1import java.util.*;
2
3public class PastaPalace {
4    public static void main(String[] args) {
5        IntervalTree reservations = new IntervalTree();
6
7        // Existing reservations (hour of day)
8        reservations.insert(17, 19, "Alice");    // 5 PM - 7 PM
9        reservations.insert(12, 14, "Bob");      // 12 PM - 2 PM
10        reservations.insert(20, 22, "Charlie");  // 8 PM - 10 PM
11        reservations.insert(13, 15, "Diana");    // 1 PM - 3 PM
12
13        // New request: Can Eve book 14:00 - 16:00?
14        IntervalTree.Node conflict = reservations.findOverlap(14, 16);
15        if (conflict != null) {
16            System.out.println("Sorry! Conflicts with " + conflict.guest
17                + "'s reservation (" + conflict.low + ":00 - "
18                + conflict.high + ":00)");
19        } else {
20            System.out.println("Reservation confirmed!");
21        }
22
23        // Find ALL conflicts for a proposed 12:30 - 14:30 slot
24        List<IntervalTree.Node> allConflicts =
25            reservations.findAllOverlaps(12, 15);
26        System.out.println("Conflicts found: " + allConflicts.size());
27        for (IntervalTree.Node c : allConflicts) {
28            System.out.println("  - " + c.guest + " ("
29                + c.low + ":00 - " + c.high + ":00)");
30        }
31    }
32}

Dry Run / Trace

Building the tree with reservations:

java
1Insert Alice [17, 19]:
2         [17,19] max=19
3          (Alice)
4
5Insert Bob [12, 14]:
6         [17,19] max=19
7          /
8     [12,14] max=14
9      (Bob)
10
11Insert Charlie [20, 22]:
12         [17,19] max=22     <-- max updated! 22 > 19
13          /         \
14     [12,14]     [20,22]
15      max=14      max=22
16      (Bob)      (Charlie)
17
18Insert Diana [13, 15]:
19              [17,19] max=22
20               /         \
21          [12,14]      [20,22]
22           max=15        max=22
23           (Bob)       (Charlie)
24               \
25            [13,15]
26             max=15
27             (Diana)
28Note: Bob's max updated from 14 to 15 because Diana (15) is in Bob's subtree.

Query: findOverlap(14, 16) โ€” "Can we book 2 PM to 4 PM?"

java
1Start at root: [17, 19] (Alice)
2  Does [17,19] overlap [14,16]?
3    17 < 16? NO. (17 is not less than 16)
4    No overlap with Alice.
5
6  Check left: left.maxHigh = 15. Is 15 > 14? YES.
7    Go LEFT to [12, 14] (Bob)
8
9  Does [12,14] overlap [14,16]?
10    12 < 16? YES.  14 < 14? NO. (14 is not less than 14)
11    No overlap with Bob. (They are adjacent, not overlapping!)
12
13  Check left of Bob: null. Skip.
14  Check right of Bob: [13, 15] (Diana)
15
16  Does [13,15] overlap [14,16]?
17    13 < 16? YES.  14 < 15? YES.
18    OVERLAP FOUND! Diana's reservation [13,15] conflicts.
19    Return Diana's node.

Key insight from the trace: Notice how we never even looked at Charlie's reservation

[20, 22]. The tree structure let us skip it entirely because we went left (where the

overlap actually was). This is the power of the maxHigh field โ€” it guides us to the

right branch.


Time & Space Complexity

OperationBrute ForceInterval Tree
InsertO(1) (append to list)O(log N) (balanced)
Find one overlapO(N)O(log N)
Find all K overlapsO(N)O(log N + K)
SpaceO(N)O(N)
  • Search is O(log N) for finding one overlap, because we traverse at most one path from

root to leaf in the tree (like binary search).

  • Finding ALL overlaps is O(log N + K) where K is the number of overlaps reported.
  • The tree must be balanced for O(log N) guarantees. In practice, use a self-balancing

BST (Red-Black Tree or AVL Tree) as the underlying structure.

  • Space is O(N) โ€” each interval gets one node.

Warning: An unbalanced interval tree degrades to O(N) per query. If you insert sorted

intervals (e.g., [1,2], [2,3], [3,4]...), you get a linked list. Always use a balanced

variant in production!


Common Mistakes & Gotchas

  • Getting the overlap condition wrong. The correct condition is a < d AND c < b for

intervals [a,b) and [c,d). If your intervals are closed (inclusive on both ends),

use a <= d AND c <= b. Be clear about whether endpoints are inclusive or exclusive.

  • **Forgetting to update maxHigh.** After every insert, you must propagate the max value

up the tree. Missing this breaks the search algorithm completely.

  • Not balancing the tree. A naive BST can degrade to a linked list. Use AVL or Red-Black

tree balancing, or use a library implementation.

  • Confusing "any overlap" with "all overlaps." Finding ONE overlap is O(log N). Finding

ALL overlaps requires visiting multiple branches and is O(log N + K).

  • Mixing up open and closed intervals. [9:00, 10:00) and [10:00, 11:00) do NOT

overlap (half-open intervals). But [9:00, 10:00] and [10:00, 11:00] DO overlap

(closed intervals share the endpoint 10:00). This distinction matters hugely in booking

systems.


Interview Tip

In LLD interviews, you rarely need to implement an Interval Tree from scratch. What

interviewers want to see is that you know when to use one and understand the trade-offs.

Common scenarios: meeting room schedulers, calendar conflict detection, network bandwidth

allocation, and game collision detection. A strong answer mentions: "For overlap queries on

a large number of intervals, I would use an Interval Tree to get O(log N) lookups instead

of O(N)." For simpler cases (non-overlapping intervals, like a single room's schedule), a

sorted list with binary search is often sufficient and simpler to implement.


Quick Quiz

  1. Think about it: If all reservations are non-overlapping and sorted by start time,

do you still need an Interval Tree? What simpler data structure could you use?

  1. Design question: How would you handle deleting a reservation from an Interval

Tree? What values need to be updated? (Hint: think about maxHigh.)

  1. Real-world extension: A meeting room booking system has 10 rooms. How would you

use Interval Trees to find the first AVAILABLE room for a given time slot? Would you

use one tree or ten?


Summary โ€” Key Takeaways

  • Interval Trees organize ranges in a BST with an extra maxHigh field per node that

enables efficient pruning during search.

  • Overlap detection drops from O(N) to O(log N) โ€” critical when you have thousands of

intervals and frequent queries.

  • **The overlap condition a < d AND c < b** is the foundation. Memorize it. Pay attention

to whether endpoints are inclusive or exclusive.

  • In LLD interviews, knowing WHEN to suggest an Interval Tree is more important than

coding one from scratch. Use it for: booking systems, calendar apps, resource scheduling,

and collision detection.