Talentd
FleetCode
Make and follow sheets.Sign in

My Calendar I solution and explanation

MediumArrayBinary SearchDesignOrdered Set13 min read58.4% acceptanceUpdated 9 Apr 2026
My Calendar I - Leetcode 729 - PythonNeetCodeIO · 12:57 · 19K viewsWatch on YouTube
9 more videos on this problem
  1. My Calendar I - Leetcode 729 - PythonNeetCodeIO · 12:57 · 19K views
  2. LeetCode 729. My Calendar I | Medium || Code + Explanation + ExampleCode with Alisha · 12:11 · 11K views
  3. My Calendar I | Detailed Approach | Leetcode 729 | codestorywithMIKcodestorywithMIK · 37:33 · 11K views
  4. LeetCode 729. My Calendar IHappy Coding · 21:38 · 4K views
  5. My Calendar I | Leetcode 729Techdose · 17:14 · 3K views
  6. MY CALENDAR | LEETCODE 729 | PYTHON SOLUTIONCracking FAANG · 15:46 · 2K views
  7. My Calendar I | 3 Approaches 🔥| LeetCode Solution 729Gantavya Malviya · 20:29 · 655 views
  8. My Calendar I | LeetCode 729 | Coders CampCoders Camp · 11:02 · 500 views
  9. 729. My Calendar I (LeetCode)hakunamatasq · 4:47 · 455 views
  10. 729. My Calendar I | English | MediumSimple Code - 단순코드 · 11:00 · 335 views

The problem

You are implementing a program to use as your calendar. We can add a new event if adding the event will not cause a double booking.

A double booking happens when two events have some non-empty intersection (i.e., some moment is common to both events.).

The event can be represented as a pair of integers startTime and endTime that represents a booking on the half-open interval [startTime, endTime), the range of real numbers x such that startTime <= x < endTime.

Implement the MyCalendar class:

  • MyCalendar() Initializes the calendar object.
  • boolean book(int startTime, int endTime) Returns true if the event can be added to the calendar successfully without causing a double booking. Otherwise, return false and do not add the event to the calendar.

Example 1:

Input
["MyCalendar", "book", "book", "book"]
[[], [10, 20], [15, 25], [20, 30]]
Output
[null, true, false, true]

Explanation
MyCalendar myCalendar = new MyCalendar();
myCalendar.book(10, 20); // return True
myCalendar.book(15, 25); // return False, It can not be booked because time 15 is already booked by another event.
myCalendar.book(20, 30); // return True, The event can be booked, as the first event takes every time less than 20, but not including 20.

Constraints:

  • 0 <= start < end <= 109
  • At most 1000 calls will be made to book.

Approach overview

Problem Overview: Design a calendar system that supports booking events represented as half‑open intervals [start, end). A booking should only succeed if the new event does not overlap with any existing event. The task is to efficiently detect interval conflicts while storing previously accepted bookings.

Approach 1: Brute Force with List (Time: O(n) per booking, Space: O(n))

Store all accepted bookings in a simple list of intervals. When a new booking request arrives, iterate through every stored interval and check for overlap. Two intervals overlap if start < existingEnd and end > existingStart. If any overlap is found, reject the booking; otherwise append the interval to the list. This approach relies only on basic array-style storage and linear scanning. It works well for small input sizes and is easy to implement during an interview, but performance degrades as the number of bookings grows because every operation scans the entire list.

Approach 2: Balanced Tree Using TreeMap (Time: O(log n) per booking, Space: O(n))

A more scalable solution keeps bookings sorted by start time using a balanced search tree such as TreeMap in Java or an ordered structure in C++/Python. Each key represents the start time and the value stores the end time. When inserting a new interval, you only need to check the immediate neighbors in sorted order. Use a floor search to find the previous interval and verify that its end does not exceed the new start. Then use a ceiling search to ensure the next interval does not begin before the new end. Because the structure maintains sorted order internally, each lookup and insertion runs in O(log n). This approach leverages concepts from binary search and ordered set data structures.

Some advanced implementations also model the calendar with a segment tree to support more complex booking queries, but for the constraints of My Calendar I that level of structure is unnecessary.

Recommended for interviews: The balanced tree approach is what most interviewers expect. It shows you understand ordered data structures and how to check only adjacent intervals instead of scanning the entire dataset. Starting with the brute force solution demonstrates correctness and reasoning, while optimizing to O(log n) using a tree structure demonstrates algorithmic maturity.

Approach 1: Approach 1: Brute Force with List

This approach uses a simple list to store existing bookings. For each new booking, we check against all previously booked intervals to ensure there is no overlap. If an overlap is found, the booking is denied; otherwise, it's added to the list. Although straightforward, this approach can be inefficient for many bookings, as each booking incurs a linear scan through the list of existing bookings.

class MyCalendar:
    def __init__(self):
        self.booked = []

    def book(self, start: int, end: int) -> bool:
        for s, e in self.booked:
            if start < e and end > s:
                return False
        self.booked.append((start, end))
        return True


my_calendar = MyCalendar()
print(my_calendar.book(10, 20))  # True
print(my_calendar.book(15, 25))  # False
print(my_calendar.book(20, 30))  # True
import java.util.ArrayList;
import java.util.List;

class MyCalendar {
    List<int[]> calendar;
    
    public MyCalendar() {
        calendar = new ArrayList<>();
    }
    
    public boolean book(int start, int end) {
        for (int[] event : calendar) {
            if (start < event[1] && end > event[0]) {
                return false;
            }
        }
        calendar.add(new int[]{start, end});
        return true;
    }

    public static void main(String[] args) {
        MyCalendar myCalendar = new MyCalendar();
        System.out.println(myCalendar.book(10, 20)); // True
        System.out.println(myCalendar.book(15, 25)); // False
        System.out.println(myCalendar.book(20, 30)); // True
    }
}
#include <vector>
#include <iostream>

class MyCalendar {
public:
    MyCalendar() {}

    bool book(int start, int end) {
        for (const auto &event : events) {
            if (start < event.second && end > event.first) {
                return false;
            }
        }
        events.push_back({start, end});
        return true;
    }

private:
    std::vector<std::pair<int, int>> events;
};

int main() {
    MyCalendar myCalendar;
    std::cout << std::boolalpha << myCalendar.book(10, 20) << std::endl; // True
    std::cout << myCalendar.book(15, 25) << std::endl; // False
    std::cout << myCalendar.book(20, 30) << std::endl; // True
    return 0;
}
class MyCalendar {
  constructor() {
    this.booked = [];
  }

  book(start, end) {
    for (const [s, e] of this.booked) {
      if (start < e && end > s) {
        return false;
      }
    }
    this.booked.push([start, end]);
    return true;
  }
}

const myCalendar = new MyCalendar();
console.log(myCalendar.book(10, 20)); // True
console.log(myCalendar.book(15, 25)); // False
console.log(myCalendar.book(20, 30)); // True
#include <stdbool.h>
#include <stdio.h>

#define MAX_EVENTS 1000

int booked[MAX_EVENTS][2];
int count = 0;

bool book(int start, int end) {
    for (int i = 0; i < count; i++) {
        if (start < booked[i][1] && end > booked[i][0]) {
            return false;
        }
    }
    booked[count][0] = start;
    booked[count][1] = end;
    count++;
    return true;
}

int main() {
    printf("%d\n", book(10, 20)); // True
    printf("%d\n", book(15, 25)); // False
    printf("%d\n", book(20, 30)); // True
    return 0;
}
using System;
using System.Collections.Generic;

public class MyCalendar {
    private List<Tuple<int, int>> calendar;

    public MyCalendar() {
        calendar = new List<Tuple<int, int>>();
    }

    public bool Book(int start, int end) {
        foreach (var interval in calendar) {
            if (start < interval.Item2 && end > interval.Item1) {
                return false;
            }
        }
        calendar.Add(Tuple.Create(start, end));
        return true;
    }

    public static void Main() {
        MyCalendar myCalendar = new MyCalendar();
        Console.WriteLine(myCalendar.Book(10, 20)); // True
        Console.WriteLine(myCalendar.Book(15, 25)); // False
        Console.WriteLine(myCalendar.Book(20, 30)); // True
    }
}

Python handles bookings with a list of tuples. The 'book' method checks overlaps by iterating through existing bookings and only appends if no overlap is found.

Complexity

Time Complexity: O(N) per booking.
Space Complexity: O(N).

Try this approach in the editor

Approach 2: Approach 2: Balanced Tree Using TreeMap

This approach leverages a data structure like a TreeMap (Java) or SortedDict (Python) to efficiently manage and query intervals. These structures allow us to quickly find the nearest existing intervals to check for overlaps, enabling faster insert operations compared to a simple list. By maintaining the intervals in a sorted order, we can reduce the number of overlaps checks necessary for each new booking.

from sortedcontainers import SortedDict

class MyCalendar:
    def __init__(self):
        self.calendar = SortedDict()

    def book(self, start: int, end: int) -> bool:
        if self.calendar:
            # predecessor: the largest key less than or equal to 'start'
            preds = self.calendar.bisect_right(start) - 1
            if preds != -1 and self.calendar.iloc[preds] > start:
                return False
            # successor: the smallest key greater than 'start'
            succs = self.calendar.bisect_left(start)
            if succs != len(self.calendar) and end > self.calendar.iloc[succs]:
                return False
        self.calendar[start] = end
        return True


my_calendar = MyCalendar()
print(my_calendar.book(10, 20))  # True
print(my_calendar.book(15, 25))  # False
print(my_calendar.book(20, 30))  # True
import java.util.TreeMap;

class MyCalendar {
    private TreeMap<Integer, Integer> calendar;
    
    public MyCalendar() {
        calendar = new TreeMap<>();
    }

    public boolean book(int start, int end) {
        Integer prev = calendar.floorKey(start);
        Integer next = calendar.ceilingKey(start);
    
        if ((prev == null || calendar.get(prev) <= start) && (next == null || end <= next)) {
            calendar.put(start, end);
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        MyCalendar myCalendar = new MyCalendar();
        System.out.println(myCalendar.book(10, 20)); // True
        System.out.println(myCalendar.book(15, 25)); // False
        System.out.println(myCalendar.book(20, 30)); // True
    }
}
#include <map>
#include <iostream>

class MyCalendar {
public:
    MyCalendar() {}

    bool book(int start, int end) {
        auto next = calendar.lower_bound(start);
        if (next != calendar.end() && next->first < end) {
            return false;
        }
        if (next != calendar.begin()) {
            auto prev = std::prev(next);
            if (prev->second > start) {
                return false;
            }
        }
        calendar[start] = end;
        return true;
    }

private:
    std::map<int, int> calendar;
};

int main() {
    MyCalendar myCalendar;
    std::cout << myCalendar.book(10, 20) << std::endl; // True
    std::cout << myCalendar.book(15, 25) << std::endl; // False
    std::cout << myCalendar.book(20, 30) << std::endl; // True
    return 0;
}

The Python solution uses 'SortedDict' from the 'sortedcontainers' module for efficient sorting and searching. It checks adjacent intervals using 'bisect_right' and 'bisect_left', minimizing overlap checks.

Complexity

Time Complexity: O(log N) per booking using SortedDict.
Space Complexity: O(N).

Try this approach in the editor

Approach 3: Ordered Set

We can use an ordered set to store the schedule. An ordered set can perform insert, delete, and search operations in O(log n) time. The elements in the ordered set are sorted by the endTime of the schedule in ascending order.

When calling the book(start, end) method, we search for the first schedule in the ordered set with an end time greater than start. If it exists and its start time is less than end, it means there is a double booking, and we return false. Otherwise, we insert end as the key and start as the value into the ordered set and return true.

The time complexity is O(n times log n), and the space complexity is O(n). Here, n is the number of schedules.

class MyCalendar {
    tm: TreeMap<number, number>;
    constructor() {
        this.tm = new TreeMap<number, number>();
    }

    book(startTime: number, endTime: number): boolean {
        const e = this.tm.higher(startTime);
        if (e && e[1] < endTime) {
            return false;
        }
        this.tm.set(endTime, startTime);
        return true;
    }
}

type Compare<T> = (lhs: T, rhs: T) => number;

class RBTreeNode<T = number> {
    data: T;
    count: number;
    left: RBTreeNode<T> | null;
    right: RBTreeNode<T> | null;
    parent: RBTreeNode<T> | null;
    color: number;
    constructor(data: T) {
        this.data = data;
        this.left = this.right = this.parent = null;
        this.color = 0;
        this.count = 1;
    }

    sibling(): RBTreeNode<T> | null {
        if (!this.parent) return null; // sibling null if no parent
        return this.isOnLeft() ? this.parent.right : this.parent.left;
    }

    isOnLeft(): boolean {
        return this === this.parent!.left;
    }

    hasRedChild(): boolean {
        return (
            Boolean(this.left && this.left.color === 0) ||
            Boolean(this.right && this.right.color === 0)
        );
    }
}

class RBTree<T> {
    root: RBTreeNode<T> | null;
    lt: (l: T, r: T) => boolean;
    constructor(compare: Compare<T> = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0)) {
        this.root = null;
        this.lt = (l: T, r: T) => compare(l, r) < 0;
    }

    rotateLeft(pt: RBTreeNode<T>): void {
        const right = pt.right!;
        pt.right = right.left;

        if (pt.right) pt.right.parent = pt;
        right.parent = pt.parent;

        if (!pt.parent) this.root = right;
        else if (pt === pt.parent.left) pt.parent.left = right;
        else pt.parent.right = right;

        right.left = pt;
        pt.parent = right;
    }

    rotateRight(pt: RBTreeNode<T>): void {
        const left = pt.left!;
        pt.left = left.right;

        if (pt.left) pt.left.parent = pt;
        left.parent = pt.parent;

        if (!pt.parent) this.root = left;
        else if (pt === pt.parent.left) pt.parent.left = left;
        else pt.parent.right = left;

        left.right = pt;
        pt.parent = left;
    }

    swapColor(p1: RBTreeNode<T>, p2: RBTreeNode<T>): void {
        const tmp = p1.color;
        p1.color = p2.color;
        p2.color = tmp;
    }

    swapData(p1: RBTreeNode<T>, p2: RBTreeNode<T>): void {
        const tmp = p1.data;
        p1.data = p2.data;
        p2.data = tmp;
    }

    fixAfterInsert(pt: RBTreeNode<T>): void {
        let parent = null;
        let grandParent = null;

        while (pt !== this.root && pt.color !== 1 && pt.parent?.color === 0) {
            parent = pt.parent;
            grandParent = pt.parent.parent;

            /*  Case : A
                Parent of pt is left child of Grand-parent of pt */
            if (parent === grandParent?.left) {
                const uncle = grandParent.right;

                /* Case : 1
                   The uncle of pt is also red
                   Only Recoloring required */
                if (uncle && uncle.color === 0) {
                    grandParent.color = 0;
                    parent.color = 1;
                    uncle.color = 1;
                    pt = grandParent;
                } else {
                    /* Case : 2
                       pt is right child of its parent
                       Left-rotation required */
                    if (pt === parent.right) {
                        this.rotateLeft(parent);
                        pt = parent;
                        parent = pt.parent;
                    }

                    /* Case : 3
                       pt is left child of its parent
                       Right-rotation required */
                    this.rotateRight(grandParent);
                    this.swapColor(parent!, grandParent);
                    pt = parent!;
                }
            } else {
                /* Case : B
               Parent of pt is right child of Grand-parent of pt */
                const uncle = grandParent!.left;

                /*  Case : 1
                    The uncle of pt is also red
                    Only Recoloring required */
                if (uncle != null && uncle.color === 0) {
                    grandParent!.color = 0;
                    parent.color = 1;
                    uncle.color = 1;
                    pt = grandParent!;
                } else {
                    /* Case : 2
                       pt is left child of its parent
                       Right-rotation required */
                    if (pt === parent.left) {
                        this.rotateRight(parent);
                        pt = parent;
                        parent = pt.parent;
                    }

                    /* Case : 3
                       pt is right child of its parent
                       Left-rotation required */
                    this.rotateLeft(grandParent!);
                    this.swapColor(parent!, grandParent!);
                    pt = parent!;
                }
            }
        }
        this.root!.color = 1;
    }

    delete(val: T): boolean {
        const node = this.find(val);
        if (!node) return false;
        node.count--;
        if (!node.count) this.deleteNode(node);
        return true;
    }

    deleteAll(val: T): boolean {
        const node = this.find(val);
        if (!node) return false;
        this.deleteNode(node);
        return true;
    }

    deleteNode(v: RBTreeNode<T>): void {
        const u = BSTreplace(v);

        // True when u and v are both black
        const uvBlack = (u === null || u.color === 1) && v.color === 1;
        const parent = v.parent!;

        if (!u) {
            // u is null therefore v is leaf
            if (v === this.root) this.root = null;
            // v is root, making root null
            else {
                if (uvBlack) {
                    // u and v both black
                    // v is leaf, fix double black at v
                    this.fixDoubleBlack(v);
                } else {
                    // u or v is red
                    if (v.sibling()) {
                        // sibling is not null, make it red"
                        v.sibling()!.color = 0;
                    }
                }
                // delete v from the tree
                if (v.isOnLeft()) parent.left = null;
                else parent.right = null;
            }
            return;
        }

        if (!v.left || !v.right) {
            // v has 1 child
            if (v === this.root) {
                // v is root, assign the value of u to v, and delete u
                v.data = u.data;
                v.left = v.right = null;
            } else {
                // Detach v from tree and move u up
                if (v.isOnLeft()) parent.left = u;
                else parent.right = u;
                u.parent = parent;
                if (uvBlack) this.fixDoubleBlack(u);
                // u and v both black, fix double black at u
                else u.color = 1; // u or v red, color u black
            }
            return;
        }

        // v has 2 children, swap data with successor and recurse
        this.swapData(u, v);
        this.deleteNode(u);

        // find node that replaces a deleted node in BST
        function BSTreplace(x: RBTreeNode<T>): RBTreeNode<T> | null {
            // when node have 2 children
            if (x.left && x.right) return successor(x.right);
            // when leaf
            if (!x.left && !x.right) return null;
            // when single child
            return x.left ?? x.right;
        }
        // find node that do not have a left child
        // in the subtree of the given node
        function successor(x: RBTreeNode<T>): RBTreeNode<T> {
            let temp = x;
            while (temp.left) temp = temp.left;
            return temp;
        }
    }

    fixDoubleBlack(x: RBTreeNode<T>): void {
        if (x === this.root) return; // Reached root

        const sibling = x.sibling();
        const parent = x.parent!;
        if (!sibling) {
            // No sibiling, double black pushed up
            this.fixDoubleBlack(parent);
        } else {
            if (sibling.color === 0) {
                // Sibling red
                parent.color = 0;
                sibling.color = 1;
                if (sibling.isOnLeft()) this.rotateRight(parent);
                // left case
                else this.rotateLeft(parent); // right case
                this.fixDoubleBlack(x);
            } else {
                // Sibling black
                if (sibling.hasRedChild()) {
                    // at least 1 red children
                    if (sibling.left && sibling.left.color === 0) {
                        if (sibling.isOnLeft()) {
                            // left left
                            sibling.left.color = sibling.color;
                            sibling.color = parent.color;
                            this.rotateRight(parent);
                        } else {
                            // right left
                            sibling.left.color = parent.color;
                            this.rotateRight(sibling);
                            this.rotateLeft(parent);
                        }
                    } else {
                        if (sibling.isOnLeft()) {
                            // left right
                            sibling.right!.color = parent.color;
                            this.rotateLeft(sibling);
                            this.rotateRight(parent);
                        } else {
                            // right right
                            sibling.right!.color = sibling.color;
                            sibling.color = parent.color;
                            this.rotateLeft(parent);
                        }
                    }
                    parent.color = 1;
                } else {
                    // 2 black children
                    sibling.color = 0;
                    if (parent.color === 1) this.fixDoubleBlack(parent);
                    else parent.color = 1;
                }
            }
        }
    }

    insert(data: T): boolean {
        // search for a position to insert
        let parent = this.root;
        while (parent) {
            if (this.lt(data, parent.data)) {
                if (!parent.left) break;
                else parent = parent.left;
            } else if (this.lt(parent.data, data)) {
                if (!parent.right) break;
                else parent = parent.right;
            } else break;
        }

        // insert node into parent
        const node = new RBTreeNode(data);
        if (!parent) this.root = node;
        else if (this.lt(node.data, parent.data)) parent.left = node;
        else if (this.lt(parent.data, node.data)) parent.right = node;
        else {
            parent.count++;
            return false;
        }
        node.parent = parent;
        this.fixAfterInsert(node);
        return true;
    }

    search(predicate: (val: T) => boolean, direction: 'left' | 'right'): T | undefined {
        let p = this.root;
        let result = null;
        while (p) {
            if (predicate(p.data)) {
                result = p;
                p = p[direction];
            } else {
                p = p[direction === 'left' ? 'right' : 'left'];
            }
        }
        return result?.data;
    }

    find(data: T): RBTreeNode<T> | null {
        let p = this.root;
        while (p) {
            if (this.lt(data, p.data)) {
                p = p.left;
            } else if (this.lt(p.data, data)) {
                p = p.right;
            } else break;
        }
        return p ?? null;
    }

    count(data: T): number {
        const node = this.find(data);
        return node ? node.count : 0;
    }

    *inOrder(root: RBTreeNode<T> = this.root!): Generator<T, undefined, void> {
        if (!root) return;
        for (const v of this.inOrder(root.left!)) yield v;
        yield root.data;
        for (const v of this.inOrder(root.right!)) yield v;
    }

    *reverseInOrder(root: RBTreeNode<T> = this.root!): Generator<T, undefined, void> {
        if (!root) return;
        for (const v of this.reverseInOrder(root.right!)) yield v;
        yield root.data;
        for (const v of this.reverseInOrder(root.left!)) yield v;
    }
}

class TreeMap<K = number, V = unknown> {
    _size: number;
    tree: RBTree<K>;
    map: Map<K, V> = new Map();
    compare: Compare<K>;
    constructor(
        collection: Array<[K, V]> | Compare<K> = [],
        compare: Compare<K> = (l: K, r: K) => (l < r ? -1 : l > r ? 1 : 0),
    ) {
        if (typeof collection === 'function') {
            compare = collection;
            collection = [];
        }
        this._size = 0;
        this.compare = compare;
        this.tree = new RBTree(compare);
        for (const [key, val] of collection) this.set(key, val);
    }

    size(): number {
        return this._size;
    }

    has(key: K): boolean {
        return !!this.tree.find(key);
    }

    get(key: K): V | undefined {
        return this.map.get(key);
    }

    set(key: K, val: V): boolean {
        const successful = this.tree.insert(key);
        this._size += successful ? 1 : 0;
        this.map.set(key, val);
        return successful;
    }

    delete(key: K): boolean {
        const deleted = this.tree.deleteAll(key);
        this._size -= deleted ? 1 : 0;
        return deleted;
    }

    ceil(target: K): [K, V] | undefined {
        return this.toKeyValue(this.tree.search(key => this.compare(key, target) >= 0, 'left'));
    }

    floor(target: K): [K, V] | undefined {
        return this.toKeyValue(this.tree.search(key => this.compare(key, target) <= 0, 'right'));
    }

    higher(target: K): [K, V] | undefined {
        return this.toKeyValue(this.tree.search(key => this.compare(key, target) > 0, 'left'));
    }

    lower(target: K): [K, V] | undefined {
        return this.toKeyValue(this.tree.search(key => this.compare(key, target) < 0, 'right'));
    }

    first(): [K, V] | undefined {
        return this.toKeyValue(this.tree.inOrder().next().value);
    }

    last(): [K, V] | undefined {
        return this.toKeyValue(this.tree.reverseInOrder().next().value);
    }

    shift(): [K, V] | undefined {
        const first = this.first();
        if (first === undefined) return undefined;
        this.delete(first[0]);
        return first;
    }

    pop(): [K, V] | undefined {
        const last = this.last();
        if (last === undefined) return undefined;
        this.delete(last[0]);
        return last;
    }

    toKeyValue(key: K): [K, V];
    toKeyValue(key: undefined): undefined;
    toKeyValue(key: K | undefined): [K, V] | undefined;
    toKeyValue(key: K | undefined): [K, V] | undefined {
        return key != null ? [key, this.map.get(key)!] : undefined;
    }

    *[Symbol.iterator](): Generator<[K, V], void, void> {
        for (const key of this.keys()) yield this.toKeyValue(key);
    }

    *keys(): Generator<K, void, void> {
        for (const key of this.tree.inOrder()) yield key;
    }

    *values(): Generator<V, undefined, void> {
        for (const key of this.keys()) yield this.map.get(key)!;
        return undefined;
    }

    *rkeys(): Generator<K, undefined, void> {
        for (const key of this.tree.reverseInOrder()) yield key;
        return undefined;
    }

    *rvalues(): Generator<V, undefined, void> {
        for (const key of this.rkeys()) yield this.map.get(key)!;
        return undefined;
    }
}

/**
 * Your MyCalendar object will be instantiated and called as such:
 * var obj = new MyCalendar()
 * var param_1 = obj.book(startTime,endTime)
 */
type MyCalendar struct {
	rbt *redblacktree.Tree
}

func Constructor() MyCalendar {
	return MyCalendar{
		rbt: redblacktree.NewWithIntComparator(),
	}
}

func (this *MyCalendar) Book(startTime int, endTime int) bool {
	if p, ok := this.rbt.Ceiling(startTime + 1); ok && p.Value.(int) < endTime {
		return false
	}
	this.rbt.Put(endTime, startTime)
	return true
}

/**
 * Your MyCalendar object will be instantiated and called as such:
 * obj := Constructor();
 * param_1 := obj.Book(startTime,endTime);
 */
use std::collections::BTreeMap;

struct MyCalendar {
    tm: BTreeMap<i32, i32>,
}

impl MyCalendar {
    fn new() -> Self {
        MyCalendar {
            tm: BTreeMap::new(),
        }
    }

    fn book(&mut self, start_time: i32, end_time: i32) -> bool {
        if let Some((&key, &value)) = self.tm.range(start_time + 1..).next() {
            if value < end_time {
                return false;
            }
        }
        self.tm.insert(end_time, start_time);
        true
    }
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Approach 1: Brute Force with List

Time Complexity: O(N) per booking.
Space Complexity: O(N).

Approach 2: Balanced Tree Using TreeMap

Time Complexity: O(log N) per booking using SortedDict.
Space Complexity: O(N).

Ordered Set–

Complexity analysis

ApproachTimeSpaceWhen to Use
Brute Force with ListO(n) per bookingO(n)Simple implementation or small number of bookings
Balanced Tree (TreeMap / Ordered Map)O(log n) per bookingO(n)Large number of bookings where efficient neighbor lookup is required

Frequently asked questions

Is My Calendar I easy or hard?

My Calendar I is classified as Medium difficulty. The brute force solution is straightforward, but identifying the optimal ordered map approach and handling interval edge cases requires a solid understanding of search trees and interval logic.

My Calendar I Python/Java solution

Python solutions typically simulate an ordered structure with sorted lists or libraries like bisect, while Java solutions use TreeMap for efficient floor and ceiling queries. Both implementations achieve O(log n) insertion and overlap checks when using an ordered map.

How to solve My Calendar I in O(log n)?

Store events in a balanced ordered map keyed by start time. For a new interval, find the previous event using a floor lookup and ensure its end time does not exceed the new start. Then check the next event using a ceiling lookup to ensure its start is not before the new end. These operations run in O(log n).

What is the best approach for My Calendar I?

The balanced tree approach using TreeMap or an ordered map is the most efficient and commonly expected solution. By storing events sorted by start time, you only check the immediate previous and next intervals for overlap. Each booking runs in O(log n) time with O(n) space for storing events.

Is My Calendar I asked at Google/Amazon/Meta?

My Calendar I and its follow‑ups appear frequently in system design and data structure interviews at companies like Google, Amazon, and Meta. Interviewers use it to test interval overlap detection and familiarity with ordered data structures.

What data structure is used in My Calendar I?

Common implementations use a list of intervals for the brute force solution or a balanced binary search tree such as TreeMap, ordered map, or ordered set. These structures allow efficient neighbor searches when inserting new intervals.

What is the time complexity of My Calendar I?

The brute force list approach takes O(n) time per booking because it scans every existing event to check for overlap. Using a balanced tree like TreeMap reduces this to O(log n) per booking since it only checks neighboring intervals in the sorted structure.

Comments

Now write it yourselfClose this page and solve My Calendar I in the editor, without looking.
Practise My Calendar I