{x}
blog image

Exam Room

There is an exam room with n seats in a single row labeled from 0 to n - 1.

When a student enters the room, they must sit in the seat that maximizes the distance to the closest person. If there are multiple such seats, they sit in the seat with the lowest number. If no one is in the room, then the student sits at seat number 0.

Design a class that simulates the mentioned exam room.

Implement the ExamRoom class:

  • ExamRoom(int n) Initializes the object of the exam room with the number of the seats n.
  • int seat() Returns the label of the seat at which the next student will set.
  • void leave(int p) Indicates that the student sitting at seat p will leave the room. It is guaranteed that there will be a student sitting at seat p.

 

Example 1:

Input
["ExamRoom", "seat", "seat", "seat", "seat", "leave", "seat"]
[[10], [], [], [], [], [4], []]
Output
[null, 0, 9, 4, 2, null, 5]

Explanation
ExamRoom examRoom = new ExamRoom(10);
examRoom.seat(); // return 0, no one is in the room, then the student sits at seat number 0.
examRoom.seat(); // return 9, the student sits at the last seat number 9.
examRoom.seat(); // return 4, the student sits at the last seat number 4.
examRoom.seat(); // return 2, the student sits at the last seat number 2.
examRoom.leave(4);
examRoom.seat(); // return 5, the student sits at the last seat number 5.

 

Constraints:

  • 1 <= n <= 109
  • It is guaranteed that there is a student sitting at seat p.
  • At most 104 calls will be made to seat and leave.

Solution Explanation: Exam Room

This problem requires designing a data structure to efficiently manage student seating in an exam room. The goal is to always seat the next student in the seat that maximizes the distance to the nearest occupied seat.

Approach: Ordered Set (TreeSet in Java, SortedList in Python, similar structures in other languages)

The most efficient approach uses an ordered set to track the available seating intervals. An ordered set allows for logarithmic time complexity operations (insertion, deletion, finding the minimum/maximum).

  1. Data Structure: We'll use an ordered set to store intervals representing available seats. Each interval is represented as a pair (start, end), where start and end are seat indices. We also need a way to quickly find the left and right neighbors of occupied seats for efficient deletion. Hashmaps (left, right) achieve this.

  2. seat() function:

    • Find the largest interval in the ordered set (this represents the maximum distance to the nearest occupied seat).
    • Calculate the midpoint of the largest interval. This is where the new student will sit. Special cases are handled for intervals at the beginning and end of the room.
    • Insert the new student's seat into the left and right hashmaps.
    • Split the largest interval into two smaller intervals and reinsert them into the ordered set.
  3. leave(p) function:

    • Find the interval containing the seat p to be vacated in the ordered set.
    • Remove this interval from the ordered set.
    • Merge the neighboring intervals (before and after p) into a single larger interval and reinsert it into the ordered set. The left and right hashmaps make this merge operation very efficient.

Time and Space Complexity

  • Time Complexity:
    • seat(): O(log n) - Finding the largest interval and inserting/deleting from the ordered set are logarithmic operations.
    • leave(p): O(log n) - Removing and inserting intervals into the ordered set are logarithmic operations.
  • Space Complexity: O(k), where k is the number of occupied seats. The ordered set and hashmaps will store at most k intervals and k occupied seat locations. In the worst case k can be n/2 (if students sit alternately), so space complexity can be O(n).

Code Examples

The code examples below demonstrate the implementation in several programming languages:

Python (using SortedList from the sortedcontainers library):

from sortedcontainers import SortedList
 
class ExamRoom:
    # ... (implementation as shown in the original response) ...

Java (using TreeSet):

import java.util.*;
 
class ExamRoom {
    // ... (implementation as shown in the original response) ...
}

C++ (using std::set):

#include <set>
#include <unordered_map>
 
class ExamRoom {
    // ... (implementation as shown in the original response) ...
};

Go (using a custom Red-Black tree or a suitable library):

package main
 
import (
	"container/heap"
	"fmt"
	"math"
)
 
type ExamRoom struct {
	n int
	h *minHeap
	s map[int]bool // occupied seats
}
 
type minHeapItem struct {
	dist int
	seat int
}
type minHeap []*minHeapItem
 
func (h minHeap) Len() int           { return len(h) }
func (h minHeap) Less(i, j int) bool { return h[i].dist < h[j].dist }
func (h minHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
func (h *minHeap) Push(x any)       { *h = append(*h, x.(*minHeapItem)) }
func (h *minHeap) Pop() any {
	old := *h
	n := len(old)
	item := old[n-1]
	old[n-1] = nil // avoid memory leak
	*h = old[0 : n-1]
	return item
}
func Constructor(n int) ExamRoom {
	er := ExamRoom{n: n, s: make(map[int]bool), h: &minHeap{}}
	heap.Push(er.h, &minHeapItem{dist: n - 1, seat: 0})
	return er
}
func (this *ExamRoom) Seat() int {
	item := heap.Pop(this.h).(*minHeapItem)
	dist := item.dist
	seat := item.seat
	if dist == 0 {
		return 0
	}
	newSeat := (seat + item.seat + dist) / 2
	this.s[newSeat] = true
	heap.Push(this.h, &minHeapItem{dist: newSeat - seat, seat: seat})
	heap.Push(this.h, &minHeapItem{dist: dist - (newSeat - seat), seat: newSeat})
	return newSeat
}
func (this *ExamRoom) Leave(p int) {
	delete(this.s, p)
	// rebuild the minHeap
	this.h = &minHeap{}
	seats := make([]int, 0, len(this.s))
	for k := range this.s {
		seats = append(seats, k)
	}
	sort.Ints(seats)
	for i := 0; i < len(seats)-1; i++ {
		heap.Push(this.h, &minHeapItem{dist: seats[i+1] - seats[i] - 1, seat: seats[i]})
	}
	if len(seats) == 0 {
		heap.Push(this.h, &minHeapItem{dist: this.n - 1, seat: 0})
	} else if len(seats) > 0 {
		heap.Push(this.h, &minHeapItem{dist: seats[0], seat: -1})
		heap.Push(this.h, &minHeapItem{dist: this.n - 1 - seats[len(seats)-1], seat: seats[len(seats)-1]})
	}
 
}
 
func main() {
	examRoom := Constructor(10)
	fmt.Println(examRoom.Seat()) //0
	fmt.Println(examRoom.Seat()) //9
	fmt.Println(examRoom.Seat()) //4
	fmt.Println(examRoom.Seat()) //2
	examRoom.Leave(4)
	fmt.Println(examRoom.Seat()) //5
 
}

TypeScript (using a custom implementation of a Red-Black tree or a suitable library):

// ... (implementation as shown in the original response but using a suitable ordered set/tree implementation) ...
 

Remember that the Go and TypeScript solutions require you to either use a suitable library providing an ordered set/balanced tree implementation or implement one yourself. The Python and Java solutions use built-in or readily available libraries. The choice of library will influence the exact details of the implementation.