def busiestServers(self, k: int, arrival: List[int], load: List[int]) -> List[int]: """ 1. Check whether a server is available now - vector/array O(1) - Treeset O(logk) 2. Find next available server - vector/array O(k) - Treeset O(logk) Use a treeset to track the available servers A min heap to track the servers that are handling requests Key: release time/ value: server id At arrival time t, release all servers that have release times <= t Time O(nlogk) Space O(k) """ pq = [] count = [0] * k available = SortedList() for i in range(k): available.add(i) for i, (arrival_time, request_load) in enumerate(zip(arrival, load)): while pq and arrival_time >= pq[0][0]: available.add(heapq.heappop(pq)[1]) server = i % k idx = available.bisect_left(server) if idx == available.__len__(): # if did not find server if not available: continue else: idx = 0 selected = available[idx] available.remove(selected) count[selected] += 1 heapq.heappush(pq, (arrival_time + request_load, selected)) max_request = max(count) ans = [] for i, c in enumerate(count): if c == max_request: ans.append(i) return ans
class IntervalList: def __init__(self, intervals): self.intervals = SortedList([], key=_get_lower) for interval in intervals: self.unite(interval) def unite(self, interval): """ Inserts the given interval into the list of interval. Takes care to merge any adjacent intervals. """ first, last = self._intersect_continuous(interval) if first != last: interval.lower = min(interval.lower, self.intervals[first].lower) interval.upper = max(interval.upper, self.intervals[last - 1].upper) del self.intervals[first:last] self.intervals.add(interval) def _intersect_continuous(self, interval): """ Finds all intervals whose intersection with interval is not empty; also includes the two intervals the the most left and right if their bounds are directly adjacent to the interval. For example, if intervals contains: [..., [1, 3), [6, 8), ...] and this function is called with [3, 6), it includes the two intervals. """ first = self.intervals.bisect_left(interval) last = first while first > 0 and \ self.intervals[first - 1].upper >= interval.lower: first -= 1 while last < len(self.intervals) and \ self.intervals[last].lower <= interval.upper: last += 1 return first, last def _intersect(self, interval): """ Finds all intervals whose intersection with interval is not empty. """ first = self.intervals.bisect_left(interval) last = first while first > 0 and \ self.intervals[first - 1].upper > interval.lower: first -= 1 while last < len(self.intervals) and \ self.intervals[last].lower < interval.upper: last += 1 return first, last def subtract(self, interval): """ Removes the given interval from all intervals. Takes care to remove empty intervals. """ first, last = self._intersect(interval) if last - first == 1: new_intervals = self.intervals[first].subtract(interval) del self.intervals[first] self.intervals.update(new_intervals) elif last - first != 0: if self.intervals[first].lower != interval.lower: self.intervals[first].upper = interval.lower if self.intervals[first].length > 0: first = min(first + 1, len(self.intervals)) if self.intervals[last - 1].upper != interval.upper: self.intervals[last - 1].lower = interval.upper if self.intervals[last - 1].length > 0: last = max(last - 1, 0) del self.intervals[first:last] def find(self, value): """ Returns the interval that contains the given value. """ index = self.intervals.bisect_left(value) if index < len( self.intervals) and self.intervals[index].lower == value: return self.intervals[index] if index > 0 and self.intervals[index - 1].contains(value): return self.intervals[index - 1] return None def contains(self, interval): """ Returns true if the list of intervals has a non-empty intersection with the given interval. """ first, last = self._intersect(interval) return first != last def __iter__(self): return self.intervals.__iter__() def __len__(self): return self.intervals.__len__()