Facebook Pixel

1845. Seat Reservation Manager

Problem Description

You need to design a seat reservation system that manages n seats numbered from 1 to n.

The system should support the following operations through a SeatManager class:

  1. Constructor SeatManager(int n): Initialize the system with n seats (numbered 1 through n). All seats start as available/unreserved.

  2. Method reserve(): Find and reserve the smallest-numbered seat that is currently unreserved. Return the seat number that was reserved.

  3. Method unreserve(int seatNumber): Make the specified seat number available again (unreserve it).

The key requirement is that when reserving a seat, you must always select the smallest-numbered unreserved seat available.

For example, if seats 1, 3, and 5 are available, calling reserve() should reserve seat 1 and return 1. If someone then unreserves seat 2, the next call to reserve() would reserve seat 2 (not seat 3), since 2 is now the smallest available seat number.

The solution uses a min-heap (priority queue) to efficiently track and retrieve the smallest available seat number. Initially, all seat numbers from 1 to n are added to the heap. When reserve() is called, the smallest element is popped from the heap. When unreserve(seatNumber) is called, that seat number is pushed back onto the heap to make it available again.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The core challenge is efficiently finding the smallest unreserved seat number at any given time. Let's think through different approaches:

If we used a simple boolean array to track which seats are reserved, finding the smallest unreserved seat would require scanning through the array from the beginning each time, taking O(n) time for each reservation.

We need a data structure that can:

  1. Give us the minimum value quickly
  2. Allow us to remove that minimum value
  3. Allow us to add values back when seats are unreserved

This naturally points us toward a min-heap (priority queue). A min-heap maintains its elements in a way where the smallest element is always at the root and can be accessed in O(1) time and removed in O(log n) time.

When we initialize the system, we have all seats from 1 to n available. We can add all these numbers to our min-heap.

When someone reserves a seat, we need the smallest available number - that's exactly what heappop() gives us by removing and returning the root of the min-heap.

When someone unreserves a seat, that seat number becomes available again. We simply add it back to our heap with heappush(), and the heap property ensures it will be positioned correctly among other available seats.

The beauty of this approach is that the heap automatically maintains the ordering for us. We don't need to sort or search - the heap structure guarantees that we can always get the minimum element efficiently.

Learn more about Heap (Priority Queue) patterns.

Solution Approach

The solution implements a seat reservation system using a min-heap (priority queue) to efficiently manage available seats.

Data Structure Choice

We use a min-heap stored in the variable self.q to maintain all available seat numbers. Python's heapq module provides heap operations on regular lists, where the smallest element is always at index 0.

Implementation Details

1. Initialization (__init__ method):

def __init__(self, n: int):
    self.q = list(range(1, n + 1))
  • Create a list containing all seat numbers from 1 to n using range(1, n + 1)
  • This list represents our initial heap with all seats available
  • Since the list is already sorted in ascending order, it satisfies the min-heap property without needing heapification

2. Reserving a Seat (reserve method):

def reserve(self) -> int:
    return heappop(self.q)
  • Use heappop() to remove and return the smallest element from the heap
  • This operation maintains the heap property after removal
  • Time complexity: O(log n) where n is the number of available seats

3. Unreserving a Seat (unreserve method):

def unreserve(self, seatNumber: int) -> None:
    heappush(self.q, seatNumber)
  • Use heappush() to add the seat number back to the heap
  • The heap automatically reorganizes to maintain the min-heap property
  • Time complexity: O(log n) where n is the number of available seats

Why This Works

The min-heap guarantees that:

  • The smallest available seat number is always at the root (index 0)
  • Both insertion and extraction of the minimum maintain logarithmic time complexity
  • No manual sorting or searching is required - the heap structure handles ordering automatically

Complexity Analysis

  • Space Complexity: O(n) to store at most n seat numbers
  • Time Complexity:
    • Initialization: O(n) to create the initial list
    • Reserve: O(log n) for heap pop operation
    • Unreserve: O(log n) for heap push operation

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example with a seat manager for 5 seats.

Initial Setup: SeatManager(5)

  • We create a min-heap with seats [1, 2, 3, 4, 5]
  • Heap visualization: [1, 2, 3, 4, 5] (smallest at front)

Operation 1: reserve()

  • Call heappop() which removes and returns the smallest element (1)
  • Heap after: [2, 3, 4, 5]
  • Returns: 1 (seat 1 is now reserved)

Operation 2: reserve()

  • Call heappop() which removes and returns the smallest element (2)
  • Heap after: [3, 4, 5]
  • Returns: 2 (seat 2 is now reserved)

Operation 3: unreserve(1)

  • Call heappush(1) to add seat 1 back to available seats
  • The heap reorganizes to maintain min-heap property
  • Heap after: [1, 3, 4, 5] (notice 1 is back at the front)

Operation 4: reserve()

  • Call heappop() which removes and returns the smallest element (1)
  • Heap after: [3, 4, 5]
  • Returns: 1 (seat 1 is reserved again - it was the smallest available)

Operation 5: unreserve(2)

  • Call heappush(2) to add seat 2 back
  • The heap reorganizes to place 2 in the correct position
  • Heap after: [2, 3, 4, 5]

Operation 6: reserve()

  • Call heappop() which removes and returns the smallest element (2)
  • Heap after: [3, 4, 5]
  • Returns: 2

This example demonstrates how the min-heap always maintains the smallest available seat at the root, making it efficient to find and reserve the lowest-numbered available seat. When seats are unreserved, they're automatically placed in the correct position within the heap structure.

Solution Implementation

1import heapq
2
3class SeatManager:
4    def __init__(self, n: int):
5        # Initialize a min-heap with all available seats from 1 to n
6        # Using a list comprehension to create seats [1, 2, 3, ..., n]
7        self.available_seats = list(range(1, n + 1))
8        # Note: The list is already a valid min-heap since it's in ascending order
9  
10    def reserve(self) -> int:
11        # Reserve and return the smallest available seat number
12        # heappop removes and returns the smallest element from the heap
13        return heapq.heappop(self.available_seats)
14  
15    def unreserve(self, seatNumber: int) -> None:
16        # Return a previously reserved seat back to the available pool
17        # heappush adds the seat back while maintaining heap property
18        heapq.heappush(self.available_seats, seatNumber)
19
20
21# Your SeatManager object will be instantiated and called as such:
22# obj = SeatManager(n)
23# param_1 = obj.reserve()
24# obj.unreserve(seatNumber)
25
1/**
2 * A seat reservation system that manages n seats numbered from 1 to n.
3 * Uses a min-heap to efficiently track and allocate the smallest available seat number.
4 */
5class SeatManager {
6    // Min-heap to store available seat numbers in ascending order
7    private PriorityQueue<Integer> availableSeats = new PriorityQueue<>();
8
9    /**
10     * Initializes the SeatManager with n available seats.
11     * All seats from 1 to n are initially unreserved.
12     * 
13     * @param n The total number of seats to manage
14     */
15    public SeatManager(int n) {
16        // Add all seat numbers from 1 to n to the priority queue
17        for (int seatNumber = 1; seatNumber <= n; seatNumber++) {
18            availableSeats.offer(seatNumber);
19        }
20    }
21
22    /**
23     * Reserves the smallest available seat number.
24     * 
25     * @return The seat number that has been reserved
26     */
27    public int reserve() {
28        // Remove and return the smallest available seat number
29        return availableSeats.poll();
30    }
31
32    /**
33     * Unreserves the specified seat, making it available again.
34     * 
35     * @param seatNumber The seat number to unreserve
36     */
37    public void unreserve(int seatNumber) {
38        // Add the seat number back to the available seats
39        availableSeats.offer(seatNumber);
40    }
41}
42
43/**
44 * Your SeatManager object will be instantiated and called as such:
45 * SeatManager obj = new SeatManager(n);
46 * int param_1 = obj.reserve();
47 * obj.unreserve(seatNumber);
48 */
49
1class SeatManager {
2public:
3    /**
4     * Constructor: Initialize the seat manager with n available seats numbered from 1 to n
5     * @param n: Total number of seats available
6     */
7    SeatManager(int n) {
8        // Initialize the min-heap with all available seat numbers (1 to n)
9        for (int i = 1; i <= n; ++i) {
10            available_seats_.push(i);
11        }
12    }
13
14    /**
15     * Reserve the smallest available seat number
16     * @return: The reserved seat number
17     */
18    int reserve() {
19        // Get the smallest available seat number from the min-heap
20        int seat_number = available_seats_.top();
21        available_seats_.pop();
22        return seat_number;
23    }
24
25    /**
26     * Unreserve a specific seat, making it available again
27     * @param seatNumber: The seat number to be unreserved
28     */
29    void unreserve(int seatNumber) {
30        // Add the seat back to the available seats pool
31        available_seats_.push(seatNumber);
32    }
33
34private:
35    // Min-heap to maintain available seats in ascending order
36    // Using greater<int> comparator to create a min-heap (smallest element at top)
37    std::priority_queue<int, std::vector<int>, std::greater<int>> available_seats_;
38};
39
40/**
41 * Your SeatManager object will be instantiated and called as such:
42 * SeatManager* obj = new SeatManager(n);
43 * int param_1 = obj->reserve();
44 * obj->unreserve(seatNumber);
45 */
46
1// MinPriorityQueue to store available seats in ascending order
2let seatQueue: typeof MinPriorityQueue;
3
4/**
5 * Initialize the seat manager with n seats numbered from 1 to n
6 * All seats are initially available
7 * @param n - Total number of seats to manage
8 */
9function initializeSeatManager(n: number): void {
10    seatQueue = new MinPriorityQueue();
11  
12    // Add all seats from 1 to n to the priority queue
13    for (let seatNumber = 1; seatNumber <= n; seatNumber++) {
14        seatQueue.enqueue(seatNumber);
15    }
16}
17
18/**
19 * Reserve the smallest available seat number
20 * @returns The reserved seat number
21 */
22function reserve(): number {
23    // Remove and return the smallest available seat number
24    const reservedSeat = seatQueue.dequeue().element;
25    return reservedSeat;
26}
27
28/**
29 * Unreserve a previously reserved seat, making it available again
30 * @param seatNumber - The seat number to unreserve
31 */
32function unreserve(seatNumber: number): void {
33    // Add the seat back to the available seats queue
34    seatQueue.enqueue(seatNumber);
35}
36

Time and Space Complexity

Time Complexity:

  • __init__(n): O(n) - Creating a list with elements from 1 to n takes linear time. Although the list is treated as a min-heap, Python's list initialization doesn't require heapification when elements are already in sorted order (1 to n is already a valid min-heap).

  • reserve(): O(log n) - The heappop operation removes the minimum element from the heap and maintains the heap property, which requires O(log n) time due to the bubble-down process.

  • unreserve(seatNumber): O(log n) - The heappush operation inserts an element into the heap and maintains the heap property through bubble-up, taking O(log n) time.

Space Complexity:

  • O(n) - The heap stores up to n seat numbers in the worst case (when no seats are reserved), requiring linear space proportional to the number of seats.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Assuming Seats Start at Index 0

A common mistake is initializing the heap with seats numbered from 0 to n-1 instead of 1 to n.

Incorrect:

def __init__(self, n: int):
    self.available_seats = list(range(n))  # Creates [0, 1, 2, ..., n-1]

Correct:

def __init__(self, n: int):
    self.available_seats = list(range(1, n + 1))  # Creates [1, 2, 3, ..., n]

2. Not Handling Edge Cases in reserve()

Calling reserve() when no seats are available will cause a crash. While the problem may guarantee this won't happen, defensive programming is good practice.

Better Implementation:

def reserve(self) -> int:
    if not self.available_seats:
        return -1  # or raise an exception
    return heapq.heappop(self.available_seats)

3. Using heapify() Unnecessarily

Since range(1, n + 1) already produces a sorted list (which is a valid min-heap), calling heapq.heapify() is redundant and wastes O(n) time.

Unnecessary:

def __init__(self, n: int):
    self.available_seats = list(range(1, n + 1))
    heapq.heapify(self.available_seats)  # Not needed!

4. Using a Set Instead of a Heap

While a set can track available seats, finding the minimum requires O(n) time, making reserve() inefficient.

Inefficient Approach:

def __init__(self, n: int):
    self.available_seats = set(range(1, n + 1))

def reserve(self) -> int:
    seat = min(self.available_seats)  # O(n) operation!
    self.available_seats.remove(seat)
    return seat

5. Duplicate Unreserving

The current implementation doesn't prevent unreserving an already-available seat, which could lead to duplicate entries in the heap.

Robust Solution:

class SeatManager:
    def __init__(self, n: int):
        self.available_seats = list(range(1, n + 1))
        self.available_set = set(range(1, n + 1))  # Track available seats
  
    def reserve(self) -> int:
        seat = heapq.heappop(self.available_seats)
        self.available_set.remove(seat)
        return seat
  
    def unreserve(self, seatNumber: int) -> None:
        if seatNumber not in self.available_set:  # Prevent duplicates
            heapq.heappush(self.available_seats, seatNumber)
            self.available_set.add(seatNumber)
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

A person thinks of a number between 1 and 1000. You may ask any number questions to them, provided that the question can be answered with either "yes" or "no".

What is the minimum number of questions you needed to ask so that you are guaranteed to know the number that the person is thinking?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More