Divide Intervals Into Minimum Number of Groups

medium intervals heap sweep

Problem

You are given intervals [left, right] with both endpoints inclusive (1 ≤ left ≤ right ≤ 10⁶, up to 10⁵ intervals). Distribute all of them into groups so that no two intervals in the same group intersect — and because the endpoints are inclusive, intervals that merely touch, like [1, 5] and [5, 8], still intersect at the shared point 5.

Return the minimum number of groups needed to hold every interval.

Inputintervals = [[5,10],[6,8],[1,5],[2,3],[1,10]]
Output3
[1,10], [5,10] and [6,8] all contain the point 6, so 3 groups are unavoidable — e.g. {[1,5],[6,8]}, {[2,3],[5,10]}, {[1,10]}.

import heapq

def min_groups(intervals):
    intervals.sort()
    heap = []                            # min-heap of group end times
    for left, right in intervals:
        if heap and heap[0] < left:
            heapq.heapreplace(heap, right)   # reuse that group
        else:
            heapq.heappush(heap, right)      # open a new group
    return len(heap)
function minGroups(intervals) {
  intervals.sort((a, b) => a[0] - b[0]);
  const heap = []; // min-heap of group end times
  const push = (v) => {
    heap.push(v);
    for (let i = heap.length - 1; i > 0; ) {
      const p = (i - 1) >> 1;
      if (heap[p] <= heap[i]) break;
      [heap[p], heap[i]] = [heap[i], heap[p]]; i = p;
    }
  };
  const pop = () => {
    const top = heap[0], last = heap.pop();
    if (heap.length) heap[0] = last;
    for (let i = 0; ; ) {
      let m = i; const l = 2 * i + 1, r = 2 * i + 2;
      if (l < heap.length && heap[l] < heap[m]) m = l;
      if (r < heap.length && heap[r] < heap[m]) m = r;
      if (m === i) break;
      [heap[m], heap[i]] = [heap[i], heap[m]]; i = m;
    }
    return top;
  };
  for (const [left, right] of intervals) {
    if (heap.length && heap[0] < left) pop(); // reuse that group
    push(right);                              // its end becomes right
  }
  return heap.length;
}
int minGroups(int[][] intervals) {
    Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
    PriorityQueue<Integer> heap = new PriorityQueue<>(); // group ends
    for (int[] iv : intervals) {
        if (!heap.isEmpty() && heap.peek() < iv[0])
            heap.poll();             // reuse that group
        heap.offer(iv[1]);           // its end becomes iv[1]
    }
    return heap.size();
}
int minGroups(vector<vector<int>>& intervals) {
    sort(intervals.begin(), intervals.end());
    priority_queue<int, vector<int>, greater<int>> heap; // group ends
    for (auto& iv : intervals) {
        if (!heap.empty() && heap.top() < iv[0])
            heap.pop();              // reuse that group
        heap.push(iv[1]);            // its end becomes iv[1]
    }
    return heap.size();
}
Time: O(n log n) Space: O(n)