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:
-
Constructor
SeatManager(int n)
: Initialize the system withn
seats (numbered1
throughn
). All seats start as available/unreserved. -
Method
reserve()
: Find and reserve the smallest-numbered seat that is currently unreserved. Return the seat number that was reserved. -
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.
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:
- Give us the minimum value quickly
- Allow us to remove that minimum value
- 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
ton
usingrange(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)
wheren
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)
wheren
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 mostn
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
- Initialization:
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample 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)
- Theheappop
operation removes the minimum element from the heap and maintains the heap property, which requiresO(log n)
time due to the bubble-down process. -
unreserve(seatNumber)
:O(log n)
- Theheappush
operation inserts an element into the heap and maintains the heap property through bubble-up, takingO(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)
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
https assets algo monster cover_photos heap svg Priority Queue and Heap What is the relationship between priority queue and heap Priority Queue is an Abstract Data Type and Heap is the concrete data structure we use to implement a priority queue Priority Queue A priority queue is a data structure
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!