2406. Divide Intervals Into Minimum Number of Groups
리트코드 2406번 풀이
문제
Divide Intervals Into Minimum Number of Groups
풀이 1
아이디어
이걸 어떻게 바로 생각해내서 풀 수 있는건가요?
필요한 최소 그룹 수는 특정 지점에서 겹치는 구간들의 최대 수와 동일하다. 구간을 일일이 탐색하는 것은 시간 초과일 수 밖에 없기 때문에 다른 방법을 생각해내야 했다. (그리고 도저히 생각나지 않아서 클로드의 가이드를 참고했다…)
구간의 시작과 끝을 각각 구간에 입장하는 이벤트와 퇴장하는 이벤트라고 생각해보자. 구간이 [1,3]이라면 1에서 입장(+1)하고 3에서 퇴장(-1)하는 이벤트가 발생하는 것이다. 이 이벤트들을 시간 순으로 처리하면 특정 시점에 동시에 입장해 있는 구간들의 개수를 알 수 있다. 이 개수의 최댓값이 필요한 최소 그룹 수가 된다.
중요한 점은 같은 지점에서 입장과 퇴장이 동시에 발생할 때, 입장 이벤트를 먼저 처리해야 한다는 것이다. 문제의 구간은 닫힌 구간이기 때문에 퇴장 이벤트가 발생해도 그 지점은 여전히 구간에 포함되어 있는 상태이다. 따라서 [1,3]과 [3,5]는 3에서 겹치는 것으로 간주된다. 이를 위해 이벤트를 정렬할 때, 같은 지점에서는 입장 이벤트(+1)가 퇴장 이벤트(-1)보다 먼저 오도록 정렬해야 한다.
코드
function minGroups(intervals: number[][]): number {
const events: number[][] = [];
for (const [left, right] of intervals) {
events.push([left, +1]);
events.push([right, -1]);
}
// 같은 지점이면 입장(+1)이 먼저 — 닫힌 구간이므로 [1,3]과 [3,5]는 겹침
events.sort((a, b) => a[0] - b[0] || b[1] - a[1]);
let max = 1;
let count = 0;
for (const [_, event] of events) {
count += event;
max = Math.max(max, count);
}
return max;
}시간 / 공간 복잡도
- 시간 복잡도: O(n log n) — 이벤트 정렬
- 공간 복잡도: O(n) — 이벤트 배열
풀이 2
아이디어
풀이 1과 동일한 아이디어를 투 포인터로도 풀 수 있다.
이벤트를 2D 배열로 저장하는 대신, 구간의 시작과 끝을 각각 다른 배열로 저장해서 정렬한 뒤에, 두 배열을 투 포인터로 탐색하면서 겹치는 구간의 개수를 세는 방식이다.
function minGroups(intervals: number[][]): number {
const starts = intervals.map(([l]) => l).sort((a, b) => a - b);
const ends = intervals.map(([, r]) => r).sort((a, b) => a - b);
let max = 0;
let count = 0;
let j = 0;
for (let i = 0; i < starts.length; i++) {
if (starts[i] <= ends[j])
// 닫힘 구간이니까 <= 조건을 써야 함.
count++; // 입장: 새 구간 시작
else j++; // 퇴장: 앞선 구간 종료
max = Math.max(max, count);
}
return max;
}더 직관적인 방법이라며 소개받은 방법이지만, 나는 좀 떠올리기 힘든 방법이라고 생각한다…