[LeetCode] 855. Exam Room 考场
In an exam room, there are N
seats in a single row, numbered 0, 1, 2, ..., 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. (Also, if no one is in the room, then the student sits at seat number 0.)
Return a class ExamRoom(int N)
that exposes two functions: ExamRoom.seat()
returning an int
representing what seat the student sat in, and ExamRoom.leave(int p)
representing that the student in seat number p
now leaves the room. It is guaranteed that any calls to ExamRoom.leave(p)
have a student sitting in seat p
.
Example 1:
Input: ["ExamRoom","seat","seat","seat","seat","leave","seat"], [[10],[],[],[],[],[4],[]]
Output: [null,0,9,4,2,null,5]
Explanation:
ExamRoom(10) -> null
seat() -> 0, no one is in the room, then the student sits at seat number 0.
seat() -> 9, the student sits at the last seat number 9.
seat() -> 4, the student sits at the last seat number 4.
seat() -> 2, the student sits at the last seat number 2.
leave(4) -> null
seat() -> 5, the student sits at the last seat number 5.
Note:
1 <= N <= 10^9
ExamRoom.seat()
andExamRoom.leave()
will be called at most10^4
times across all test cases.- Calls to
ExamRoom.leave(p)
are guaranteed to have a student currently sitting in seat numberp
.
在一个考场中,有N个座位在一排,当学生进入房间时,他们必须坐在与最近的人距离最大的座位上。 如果有多个这样的座位,他们就坐在座位编号最小的座位上。(另外,如果没有人在房间里,那么学生就坐在0号座位上。)实现一个类ExamRoom(int N),含有2个函数,ExamRoom.seat()返回学生应该坐的位置和ExamRoom.leave(int p)返回离开座位的学生。
解法1:最大堆, T: seat: O(logn), leave O(n),S: O(n)。用一个max heap来记录所有两个相邻位置的间距,每次进来一个学生就pop出堆顶的位置,然后分配中间位子给学生,同时用一个数组记录当前位子的学生,再把新形成的两个相邻位置push到heap。当学生离开时,从数组中找到相应位置的学生,从数组中去除,同时去除heap中由这个位置形成的两个间距距离。
解法2: Use a list L to record the index of seats where people sit. T: O(N) for seat() and leave()
seat():
1. find the biggest distance at the start, at the end and in the middle.
2. insert index of seat
3. return index
leave(p): pop out p
Java:
int N; ArrayList<Integer> L = new ArrayList<>(); public ExamRoom(int n) { N = n; } public int seat() { if (L.size() == 0) { L.add(0); return 0; } int d = Math.max(L.get(0), N - 1 - L.get(L.size() - 1)); for (int i = 0; i < L.size() - 1; ++i) d = Math.max(d, (L.get(i + 1) - L.get(i)) / 2); if (L.get(0) == d) { L.add(0, 0); return 0; } for (int i = 0; i < L.size() - 1; ++i) if ((L.get(i + 1) - L.get(i)) / 2 == d) { L.add(i + 1, (L.get(i + 1) + L.get(i)) / 2); return L.get(i + 1); } L.add(N - 1); return N - 1; } public void leave(int p) { for (int i = 0; i < L.size(); ++i) if (L.get(i) == p) L.remove(i); }
Java:
// PriorityQueue class ExamRoom { PriorityQueue<Interval> pq; int N; class Interval { int x, y, dist; public Interval(int x, int y) { this.x = x; this.y = y; if (x == -1) { this.dist = y; } else if (y == N) { this.dist = N - 1 - x; } else { this.dist = Math.abs(x - y) / 2; } } } public ExamRoom(int N) { this.pq = new PriorityQueue<>((a, b) -> a.dist != b.dist? b.dist - a.dist : a.x - b.x); this.N = N; pq.add(new Interval(-1, N)); } // O(logn): poll top candidate, split into two new intervals public int seat() { int seat = 0; Interval interval = pq.poll(); if (interval.x == -1) seat = 0; else if (interval.y == N) seat = N - 1; else seat = (interval.x + interval.y) / 2; pq.offer(new Interval(interval.x, seat)); pq.offer(new Interval(seat, interval.y)); return seat; } // O(n)Find head and tail based on p. Delete and merge two ends public void leave(int p) { Interval head = null, tail = null; List<Interval> intervals = new ArrayList<>(pq); for (Interval interval : intervals) { if (interval.x == p) tail = interval; if (interval.y == p) head = interval; if (head != null && tail != null) break; } // Delete pq.remove(head); pq.remove(tail); // Merge pq.offer(new Interval(head.x, tail.y)); } }
Python:
# Time: seat: O(logn) on average, # leave: O(logn) # Space: O(n) import heapq LEN, POS, L, R = range(4) LEFT, RIGHT = range(2) class ExamRoom(object): def __init__(self, N): """ :type N: int """ self.__num = N self.__max_heap = [(-self.__num, 0, -1, self.__num)] self.__seats = {-1: [-1, self.__num], self.__num: [-1, self.__num]} def seat(self): """ :rtype: int """ while self.__max_heap[0][L] not in self.__seats or \ self.__max_heap[0][R] not in self.__seats or \ self.__seats[self.__max_heap[0][L]][RIGHT] != \ self.__max_heap[0][R] or \ self.__seats[self.__max_heap[0][R]][LEFT] != \ self.__max_heap[0][L]: heapq.heappop(self.__max_heap) # lazy deletion curr = heapq.heappop(self.__max_heap) if curr[L] == -1 and curr[R] == self.__num: heapq.heappush(self.__max_heap, (-(curr[R]-1), curr[R]-1, curr[L]+1, curr[R])) elif curr[L] == -1: heapq.heappush(self.__max_heap, (-(curr[R]//2), curr[R]//2, curr[L]+1, curr[R])) elif curr[R] == self.__num: heapq.heappush(self.__max_heap, (-((curr[R]-1-curr[L])//2), (curr[R]-1-curr[L])//2+curr[L], curr[L], curr[R]-1)) else: heapq.heappush(self.__max_heap, (-((curr[POS]-curr[L])//2), (curr[POS]-curr[L])//2+curr[L], curr[L], curr[POS])) heapq.heappush(self.__max_heap, (-((curr[R]-curr[POS])//2), (curr[R]-curr[POS])//2+curr[POS], curr[POS], curr[R])) self.__seats[curr[POS]] = [curr[L], curr[R]] self.__seats[curr[L]][RIGHT] = curr[POS] self.__seats[curr[R]][LEFT] = curr[POS] return curr[POS] def leave(self, p): """ :type p: int :rtype: void """ neighbors = self.__seats[p] self.__seats.pop(p) if neighbors[LEFT] == -1 and neighbors[RIGHT] == self.__num: heapq.heappush(self.__max_heap, (-neighbors[RIGHT], neighbors[LEFT]+1, neighbors[LEFT], neighbors[RIGHT])) elif neighbors[LEFT] == -1: heapq.heappush(self.__max_heap, (-neighbors[RIGHT], neighbors[LEFT]+1, neighbors[LEFT], neighbors[RIGHT])) elif neighbors[RIGHT] == self.__num: heapq.heappush(self.__max_heap, (-(neighbors[RIGHT]-1-neighbors[LEFT]), neighbors[RIGHT]-1, neighbors[LEFT], neighbors[RIGHT])) else: heapq.heappush(self.__max_heap, (-((neighbors[RIGHT]-neighbors[LEFT])//2), (neighbors[RIGHT]-neighbors[LEFT])//2 + neighbors[LEFT], neighbors[LEFT], neighbors[RIGHT])) self.__seats[neighbors[LEFT]][RIGHT] = neighbors[RIGHT] self.__seats[neighbors[RIGHT]][LEFT] = neighbors[LEFT]
Python:
def __init__(self, N): self.N, self.L = N, [] def seat(self): N, L = self.N, self.L if not L: res = 0 else: d, res = L[0], 0 for a, b in zip(L, L[1:]): if (b - a) / 2 > d: d, res = (b - a) / 2, (b + a) / 2 if N - 1 - L[-1] > d: res = N - 1 bisect.insort(L, res) return res def leave(self, p): self.L.remove(p)
Python: O(log n) time for both seat() and leave() with heapq and dicts
from heapq import heappop, heappush class ExamRoom(object): def __init__(self, N): """ :type N: int """ self.N = N self.heap = [] self.avail_first = {} self.avail_last = {} self.put_segment(0, self.N - 1) def put_segment(self, first, last): if first == 0 or last == self.N - 1: priority = last - first else: priority = (last - first) // 2 segment = [-priority, first, last, True] self.avail_first[first] = segment self.avail_last[last] = segment heappush(self.heap, segment) def seat(self): """ :rtype: int """ while True: _, first, last, is_valid = heappop(self.heap) if is_valid: del self.avail_first[first] del self.avail_last[last] break if first == 0: ret = 0 if first != last: self.put_segment(first + 1, last) elif last == self.N - 1: ret = last if first != last: self.put_segment(first, last - 1) else: ret = first + (last - first) // 2 if ret > first: self.put_segment(first, ret - 1) if ret < last: self.put_segment(ret + 1, last) return ret def leave(self, p): """ :type p: int :rtype: void """ first = p last = p left = p - 1 right = p + 1 if left >= 0 and left in self.avail_last: segment_left = self.avail_last.pop(left) segment_left[3] = False first = segment_left[1] if right < self.N and right in self.avail_first: segment_right = self.avail_first.pop(right) segment_right[3] = False last = segment_right[2] self.put_segment(first, last)
C++:
int N; vector<int> L; ExamRoom(int n) { N = n; } int seat() { if (L.size() == 0) { L.push_back(0); return 0; } int d = max(L[0], N - 1 - L[L.size() - 1]); for (int i = 0; i < L.size() - 1; ++i) d = max(d, (L[i + 1] - L[i]) / 2); if (L[0] == d) { L.insert(L.begin(), 0); return 0; } for (int i = 0; i < L.size() - 1; ++i) if ((L[i + 1] - L[i]) / 2 == d) { L.insert(L.begin() + i + 1, (L[i + 1] + L[i]) / 2); return L[i + 1]; } L.push_back(N - 1); return N - 1; } void leave(int p) { for (int i = 0; i < L.size(); ++i) if (L[i] == p) L.erase(L.begin() + i); }
C++:
// Time: seat: O(logn), // leave: O(logn) // Space: O(n) class ExamRoom { public: ExamRoom(int N) : num_(N) { segment_iters_[make_pair(-1, num_)] = max_bst_.emplace(make_shared<Segment>(num_, 0, -1, num_)); seats_[-1] = make_pair(-1, num_); seats_[num_] = make_pair(-1, num_); } int seat() { const auto curr = *max_bst_.begin(); max_bst_.erase(max_bst_.begin()); segment_iters_.erase(make_pair(curr->l, curr->r)); if (curr->l == -1 && curr->r == num_) { segment_iters_[make_pair(curr->l + 1, curr->r)] = max_bst_.emplace( make_shared<Segment>(curr->r - 1, curr->r - 1, curr->l + 1, curr->r)); } else if (curr->l == -1) { segment_iters_[make_pair(curr->l + 1, curr->r)] = max_bst_.emplace( make_shared<Segment>(curr->r / 2, curr->r / 2, curr->l + 1, curr->r)); } else if (curr->r == num_) { segment_iters_[make_pair(curr->l, curr->r - 1)] = max_bst_.emplace( make_shared<Segment>((curr->r - 1 - curr->l) / 2, (curr->r - 1 - curr->l) / 2 + curr->l, curr->l, curr->r - 1)); } else { segment_iters_[make_pair(curr->l, curr->pos)] = max_bst_.emplace( make_shared<Segment>((curr->pos - curr->l) / 2, (curr->pos - curr->l) / 2 + curr->l, curr->l, curr->pos)); segment_iters_[make_pair(curr->pos, curr->r)] = max_bst_.emplace( make_shared<Segment>((curr->r - curr->pos) / 2, (curr->r - curr->pos) / 2 + curr->pos, curr->pos, curr->r)); } seats_[curr->pos] = make_pair(curr->l, curr->r); seats_[curr->l].second = curr->pos; seats_[curr->r].first = curr->pos; return curr->pos; } void leave(int p) { const auto neighbors = seats_[p]; seats_.erase(p); const auto& left_segment = make_pair(neighbors.first, p); if (segment_iters_.count(left_segment)) { max_bst_.erase(segment_iters_[left_segment]); segment_iters_.erase(left_segment); } const auto& right_segment = make_pair(p, neighbors.second); if (segment_iters_.count(right_segment)) { max_bst_.erase(segment_iters_[right_segment]); segment_iters_.erase(right_segment); } if (neighbors.first == -1 && neighbors.second == num_) { segment_iters_[neighbors] = max_bst_.emplace( make_shared<Segment>(neighbors.second, neighbors.first + 1, neighbors.first, neighbors.second)); } else if (neighbors.first == -1) { segment_iters_[neighbors] = max_bst_.emplace( make_shared<Segment>(neighbors.second, neighbors.first + 1, neighbors.first, neighbors.second)); } else if (neighbors.second == num_) { segment_iters_[neighbors] = max_bst_.emplace( make_shared<Segment>(neighbors.second - 1 - neighbors.first, neighbors.second - 1, neighbors.first, neighbors.second)); } else { segment_iters_[neighbors] = max_bst_.emplace( make_shared<Segment>((neighbors.second - neighbors.first) / 2, (neighbors.second - neighbors.first) / 2 + neighbors.first, neighbors.first, neighbors.second)); } seats_[neighbors.first].second = neighbors.second; seats_[neighbors.second].first = neighbors.first; } private: struct Segment { int dis; int pos; int l; int r; Segment(int dis, int pos, int l, int r) : dis(dis), pos(pos), l(l), r(r) { } }; template <typename T> struct Compare { bool operator()(const T& a, const T& b) { return a->dis == b->dis ? less<int>()(a->l, b->l) : greater<int>()(a->dis, b->dis); } }; template <typename T> struct PairHash { size_t operator()(const pair<T, T>& p) const { size_t seed = 0; seed ^= std::hash<T>{}(p.first) + 0x9e3779b9 + (seed<<6) + (seed>>2); seed ^= std::hash<T>{}(p.second) + 0x9e3779b9 + (seed<<6) + (seed>>2); return seed; } }; int num_; using S = shared_ptr<Segment>; multiset<S, Compare<S>> max_bst_; unordered_map<int, pair<int, int>> seats_; unordered_map<pair<int, int>, multiset<S, Compare<S>>::iterator, PairHash<int>> segment_iters_; }; // Time: seat: O(logn) on average, // leave: O(logn) // Space: O(n) class ExamRoom2 { public: ExamRoom2(int N) : num_(N) { max_bst_.emplace(make_shared<Segment>(num_, 0, -1, num_)); seats_[-1] = make_pair(-1, num_); seats_[num_] = make_pair(-1, num_); } int seat() { while (!seats_.count((*max_bst_.cbegin())->l) || !seats_.count((*max_bst_.cbegin())->r) || seats_[(*max_bst_.cbegin())->l].second != (*max_bst_.cbegin())->r || seats_[(*max_bst_.cbegin())->r].first != (*max_bst_.cbegin())->l) { max_bst_.erase(max_bst_.begin()); // lazy deletion } const auto curr = *max_bst_.begin(); max_bst_.erase(max_bst_.begin()); if (curr->l == -1 && curr->r == num_) { max_bst_.emplace( make_shared<Segment>(curr->r - 1, curr->r - 1, curr->l + 1, curr->r)); } else if (curr->l == -1) { max_bst_.emplace( make_shared<Segment>(curr->r / 2, curr->r / 2, curr->l + 1, curr->r)); } else if (curr->r == num_) { max_bst_.emplace( make_shared<Segment>((curr->r - 1 - curr->l) / 2, (curr->r - 1 - curr->l) / 2 + curr->l, curr->l, curr->r - 1)); } else { max_bst_.emplace( make_shared<Segment>((curr->pos - curr->l) / 2, (curr->pos - curr->l) / 2 + curr->l, curr->l, curr->pos)); max_bst_.emplace( make_shared<Segment>((curr->r - curr->pos) / 2, (curr->r - curr->pos) / 2 + curr->pos, curr->pos, curr->r)); } seats_[curr->pos] = make_pair(curr->l, curr->r); seats_[curr->l].second = curr->pos; seats_[curr->r].first = curr->pos; return curr->pos; } void leave(int p) { const auto neighbors = seats_[p]; seats_.erase(p); if (neighbors.first == -1 && neighbors.second == num_) { max_bst_.emplace( make_shared<Segment>(neighbors.second, neighbors.first + 1, neighbors.first, neighbors.second)); } else if (neighbors.first == -1) { max_bst_.emplace( make_shared<Segment>(neighbors.second, neighbors.first + 1, neighbors.first, neighbors.second)); } else if (neighbors.second == num_) { max_bst_.emplace( make_shared<Segment>(neighbors.second - 1 - neighbors.first, neighbors.second - 1, neighbors.first, neighbors.second)); } else { max_bst_.emplace( make_shared<Segment>((neighbors.second - neighbors.first) / 2, (neighbors.second - neighbors.first) / 2 + neighbors.first, neighbors.first, neighbors.second)); } seats_[neighbors.first].second = neighbors.second; seats_[neighbors.second].first = neighbors.first; } private: struct Segment { int dis; int pos; int l; int r; Segment(int dis, int pos, int l, int r) : dis(dis), pos(pos), l(l), r(r) { } }; template <typename T> struct Compare { bool operator()(const T& a, const T& b) { return a->dis == b->dis ? less<int>()(a->l, b->l) : greater<int>()(a->dis, b->dis); } }; int num_; using S = shared_ptr<Segment>; multiset<S, Compare<S>> max_bst_; unordered_map<int, pair<int, int>> seats_; }; // Time: seat: O(logn) on average, // leave: O(logn) // Space: O(n) class ExamRoom3 { public: ExamRoom3(int N) : num_(N) { max_heap_.emplace(make_shared<Segment>(num_, 0, -1, num_)); seats_[-1] = make_pair(-1, num_); seats_[num_] = make_pair(-1, num_); } int seat() { while (!seats_.count(max_heap_.top()->l) || !seats_.count(max_heap_.top()->r) || seats_[max_heap_.top()->l].second != max_heap_.top()->r || seats_[max_heap_.top()->r].first != max_heap_.top()->l) { max_heap_.pop(); // lazy deletion } const auto curr = max_heap_.top(); max_heap_.pop(); if (curr->l == -1 && curr->r == num_) { max_heap_.emplace( make_shared<Segment>(curr->r - 1, curr->r - 1, curr->l + 1, curr->r)); } else if (curr->l == -1) { max_heap_.emplace( make_shared<Segment>(curr->r / 2, curr->r / 2, curr->l + 1, curr->r)); } else if (curr->r == num_) { max_heap_.emplace( make_shared<Segment>((curr->r - 1 - curr->l) / 2, (curr->r - 1 - curr->l) / 2 + curr->l, curr->l, curr->r - 1)); } else { max_heap_.emplace( make_shared<Segment>((curr->pos - curr->l) / 2, (curr->pos - curr->l) / 2 + curr->l, curr->l, curr->pos)); max_heap_.emplace( make_shared<Segment>((curr->r - curr->pos) / 2, (curr->r - curr->pos) / 2 + curr->pos, curr->pos, curr->r)); } seats_[curr->pos] = make_pair(curr->l, curr->r); seats_[curr->l].second = curr->pos; seats_[curr->r].first = curr->pos; return curr->pos; } void leave(int p) { const auto neighbors = seats_[p]; seats_.erase(p); if (neighbors.first == -1 && neighbors.second == num_) { max_heap_.emplace( make_shared<Segment>(neighbors.second, neighbors.first + 1, neighbors.first, neighbors.second)); } else if (neighbors.first == -1) { max_heap_.emplace( make_shared<Segment>(neighbors.second, neighbors.first + 1, neighbors.first, neighbors.second)); } else if (neighbors.second == num_) { max_heap_.emplace( make_shared<Segment>(neighbors.second - 1 - neighbors.first, neighbors.second - 1, neighbors.first, neighbors.second)); } else { max_heap_.emplace( make_shared<Segment>((neighbors.second - neighbors.first) / 2, (neighbors.second - neighbors.first) / 2 + neighbors.first, neighbors.first, neighbors.second)); } seats_[neighbors.first].second = neighbors.second; seats_[neighbors.second].first = neighbors.first; } private: struct Segment { int dis; int pos; int l; int r; Segment(int dis, int pos, int l, int r) : dis(dis), pos(pos), l(l), r(r) { } }; template <typename T> struct Compare { bool operator()(const T& a, const T& b) { return a->dis == b->dis ? greater<int>()(a->l, b->l) : less<int>()(a->dis, b->dis); } }; int num_; using S = shared_ptr<Segment>; priority_queue<S, vector<S>, Compare<S>> max_heap_; unordered_map<int, pair<int, int>> seats_; }; /** * Your ExamRoom object will be instantiated and called as such: * ExamRoom obj = new ExamRoom(N); * int param_1 = obj.seat(); * obj.leave(p); */