435. Non-overlapping Intervals
Given an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
Note that intervals which only touch at a point are non-overlapping. For example, [1, 2] and [2, 3] are non-overlapping.
Example 1:
Input: intervals = [[1,2],[2,3],[3,4],[1,3]]
Output: 1
Explanation: [1,3] can be removed and the rest of the intervals are non-overlapping.
Example 2:
Input: intervals = [[1,2],[1,2],[1,2]]
Output: 2
Explanation: You need to remove two [1,2] to make the rest of the intervals non-overlapping.
Example 3:
Input: intervals = [[1,2],[2,3]]
Output: 0
Explanation: You don't need to remove any of the intervals since they're already non-overlapping.
Constraints:
- 1 <= intervals.length <= 105
- intervals[i].length == 2
- -5 * 104 <= starti < endi <= 5 * 104
이 문제는 **주어진 interval 배열(intervals)**에서 겹치는 구간을 제거하여 나머지 구간들이 겹치지 않도록(non-overlapping) 만드는 최소한의 구간 개수를 구하는 문제입니다.
풀이 방법
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
# Step 1: intervals 리스트를 끝나는 시간(x[1]) 기준으로 오름차순 정렬합니다.
# 정렬 후, 끝나는 시간이 빠른 구간부터 처리합니다.
# 왜 끝나는 시간 기준으로 정렬하나요?
# - 끝나는 시간이 빠른 구간을 선택하면 이후에 겹치는 구간을 최소화할 가능성이 높기 때문입니다.
# 예: intervals = [[1,3],[2,4],[3,5]] → 끝나는 시간 기준으로 정렬 → [[1,3],[2,4],[3,5]]
intervals.sort(key=lambda x: x[1])
# Step 2: 초기화
# prev_end는 이전에 선택된 구간의 끝나는 시간을 저장합니다.
# 초기값으로 -inf를 설정하여 첫 번째 구간과 항상 겹치지 않도록 보장합니다.
prev_end = float('-inf')
# remove_count는 겹치는 구간을 제거한 횟수를 저장합니다.
remove_count = 0
# Step 3: 모든 구간을 순회하며 겹치는지 확인합니다.
for start, end in intervals:
# 현재 구간의 시작점(start)이 이전 선택된 구간의 끝점(prev_end)보다 크거나 같으면 두 구간은 겹치지 않습니다.
if start >= prev_end:
# 현재 구간을 선택하고 prev_end를 현재 구간의 끝점으로 업데이트합니다.
prev_end = end
else:
# 현재 구간의 시작점이 이전 구간의 끝점보다 작다면 두 구간이 겹칩니다.
# 따라서 현재 구간을 제거하고 제거 횟수를 1 증가시킵니다.
remove_count += 1
# 왜 제거할까요?
# - 끝나는 시간이 더 빠른 구간(prev_end)을 유지하면 이후 구간과의 겹침을 줄일 가능성이 높기 때문입니다.
# - prev_end는 업데이트하지 않습니다(기존 선택된 구간을 유지합니다).
# Step 4: 제거된 구간의 총 개수를 반환합니다.
return remove_count
https://youtu.be/nONCGxWoUfM?si=77-kUfjYRH_FldB0
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
# Step 1: intervals 배열을 `시작 시간` 기준으로 정렬
# 예를 들어, intervals = [[1,3], [2,4], [3,5]]으로 정렬됨
intervals.sort()
# Step 2: 결과를 저장할 변수 초기화
res = 0 # 제거해야 하는 구간의 수
# prevEnd는 이전에 선택된 구간의 끝점을 저장
prevEnd = intervals[0][1] # 첫 번째 구간의 끝점으로 초기화
# Step 3: 두 번째 구간부터 순회
for start, end in intervals[1:]:
# 현재 구간의 시작점(start)과 이전 구간의 끝점(prevEnd)을 비교
if start >= prevEnd: # 현재 구간이 이전 구간과 겹치지 않는 경우
# 현재 구간을 선택하므로 prevEnd를 현재 구간의 끝점(end)으로 업데이트
prevEnd = end
else: # 현재 구간이 이전 구간과 겹치는 경우
# 현재 구간의 시작점(start)이 이전 구간의 끝점(prevEnd)보다 작다면 두 구간은 겹침
# 이 경우, 겹치는 구간 중 하나를 제거해야 함
res += 1 # 제거한 구간 수를 1 증가
# 겹치는 구간들 중 끝나는 시간이 더 빠른 구간을 선택해야,
# 이후 구간들이 더 많이 겹치지 않을 가능성이 높음
# 즉, `더 빨리 끝나는 구간`을 선택하여 이후 겹침을 최소화
# 이전 구간의 끝점(prevEnd)와 현재 구간의 끝점(end) 중 더 작은 값을 선택
prevEnd = min(end, prevEnd)
# Step 4: 제거한 구간 수 반환
return res
이 코드와 GPT 코드의 차이
- 정렬 기준:
- 내 코드: 끝나는 시간 기준으로 정렬
- 이 코드: 시작 시간 기준으로 정렬
- prevEnd 업데이트 방식:
- 내 코드: 겹치는 경우 prevEnd를 건드리지 않음.
- 이 코드: 겹치는 경우 prevEnd를 **min(end, prevEnd)**로 업데이트.
결론
- 두 방법 모두 올바르게 동작하지만, 이 코드에서는 시작 시간 기준으로 정렬 후 겹침을 해결합니다.
- 끝나는 시간 기준으로 정렬하는 방식이 직관적일 수 있지만, 두 방식 모두 효율적이고 결과는 동일합니다.
- GPT 코드가 좀 더 빠름
-----------------------------------------------------------------------------------------------------------------------------
두번째 시도
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
# 1. 끝점 기준으로 구간 정렬
intervals.sort(key=lambda x: x[1])
# 2. 겹치지 않는 구간의 개수와 이전 구간 끝점 초기화
non_overlap_count = 0
prev_end = float('-inf')
# 3. 구간 순회
for start, end in intervals:
# 현재 구간이 이전 구간과 겹치지 않는 경우
if start >= prev_end:
non_overlap_count += 1
prev_end = end # 현재 구간을 선택
# 겹치는 경우: 현재 구간은 선택하지 않음 (그리디 선택)
# 4. 전체 구간 수 - 겹치지 않는 구간 수
return len(intervals) - non_overlap_count
최종 코드
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
# end 기준으로 오름차순 정렬
intervals.sort(key=lambda x : x[1])
overlap_cnt = 0
prev_end = -float("inf")
for start, end in intervals:
if prev_end <= start:
prev_end = end
else:
overlap_cnt+=1
return overlap_cnt
'LeetCode > LeetCode75' 카테고리의 다른 글
[LeetCode 75] Medium - 901. Online Stock Span (4) | 2024.11.10 |
---|---|
[LeetCode 75] Medium - 739. Daily Temperatures (0) | 2024.11.10 |
[LeetCode 75] Medium - 1268. Search Suggestions System (0) | 2024.11.09 |
[LeetCode 75] Medium - 1318. Minimum Flips to Make a OR b Equal to c (0) | 2024.11.09 |
[LeetCode 75] Medium - 790. Domino and Tromino Tiling (5) | 2024.11.08 |