1. 題目描述
Given a collection of intervals, find the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
Note:
- You may assume the interval’s end point is always bigger than its start point.
- Intervals like [1,2] and [2,3] have borders “touching” but they don’t overlap each other.
【翻譯過來】:給定了一系列區間interval,每個區間有一個首元素start和尾元素end,並且end > start,我們需要做這樣的事情:在這一系列區間中,刪除某些區間使得剩餘的區間不存在重疊的情況,我們需要保證所刪除的區間數目最少。此外類似[1, 2]和[2, 3]這樣的邊界接觸的我們不認爲是重疊。
2. 樣例
Example1:
Input: [ [1,2], [2,3], [3,4], [1,3] ]
Output: 1
Explanation: [1,3] can be removed and the rest of intervals are non-overlapping.
Example2:
Input: [ [1,2], [1,2], [1,2] ]
Output: 2
Explanation: You need to remove two [1,2] to make the rest of intervals non-overlapping.
Example 3:
Input: [ [1,2], [2,3] ]
Output: 0
Explanation: You don't need to remove any of the intervals since they're already non-overlapping.
3. 分析
題目給出了一系列interval,這些interval也許不會按照順序排列,題目的關鍵詞提到了最小刪除interval的數量,看到這個關鍵詞,我很容易想到貪心算法。
因此,我設計的貪心算法目的是:在當前情況下,能保證當前已經訪問過的interval的集合都是不重疊的,並且刪除了最小數目的重疊interval。於是,我想到了兩個思路,後來結果表明思路1是錯誤的。
3.1. 方法1:按照start升序排列
該算法的思想是:將所有的interval根據start元素升序排列,在start相同的時候,根據end升序排列。這樣的序列我們進行這樣的操作:從第二個interval開始,當後面的元素start小於前一個元素的end,說明就會發生重疊,就要將其刪除,結果計數器counter記錄一次,反之則代表不會重疊,以此類推,遍歷一次後就會把重複的interval刪去。
此法看起來有道理,但實際是錯誤的,因爲考慮以下的情況:
Input: [ [1,2], [2,9], [3,4], [5,6] ]
/* We need only remove [2, 9] */
Right Output: 1
/* Remove [3,4] and [5,6] */
Actually Ouput: 2
3.2. 方法2:按照end升序排列
該算法的思想是:將所有的interval根據end元素升序排列,在end相同的時候,根據start升序排列。這樣的序列保證了end從最小開始,我們只需判斷每一個的interval的start是否會超過end即可,遍歷算法與上一個方法類似。
這種算法的好處是:避免了因爲end過大影響了其餘interval的情況,即有些區間因爲end過大了,甚至將很多start排在後面的區間包含了進去,這種情況下,我們應當刪除這個end大的區間,才能保證最小刪除區間的數目。
4. 源碼
struct Interval {
int start;
int end;
Interval() : start(0), end(0) {}
Interval(int s, int e) : start(s), end(e) {}
};
static bool Compare(Interval a, Interval b) {
if (a.end <= b.end) {
if (a.end < b.end) {
return true;
}
return a.start <= b.start;
}
return false;
}
int eraseOverlapIntervals(vector<Interval>& intervals) {
int counter = 0;
if (intervals.size() == 0) {
return 0;
}
sort(intervals.begin(), intervals.end(), Compare);
int reference = intervals[0].end;
for (int i = 1; i < (int)intervals.size(); i++) {
if (intervals[i].start < reference) {
counter++;
}
else {
reference = intervals[i].end;
}
}
return counter;
}
5. 心得
貪心算法一旦找到了正確的每一步的處理目標和方法就會非常有效地節省時間和效率。這道題目的難度尾medium,它的難點應該就是在於如何對intervals進行處理,如果只是單純地考慮start遞增順序遍歷,刪除元素,就會出現不能刪除最小數目的interval情況。