看到這個題我就傷心啊,去微軟面試的時候,第一個面試官讓我做的題目就是實現集合的交操作,這個集合中的元素就像這裡的interval一樣,是一段一段的。當時寫的那叫一個慘不忍睹,最後果然被拒掉了。。好好練習演算法,爭取正式招聘的時候拒一次微軟,哈哈~
說歸說,這道題其實還是比較簡單的。先考慮什麼樣子的集合是可以合并的。設兩段集合是[a, b]和[c, d],不失一般性的,假設a<c,那麼有下面幾種情況:
1. b<c,這說明兩段是完全不相交的,沒辦法合并。
2. b>=c&&b<=d,兩段相交了,這個時候新合并出的結合的頭應該是a,結尾應該是d。
3. b>d,也就是後一段完全是前一段的子段,那麼合并後的集合應該是[a, b]。
注意到,要想從頭到尾掃一遍就完成所有的合并,必須實現對所有的段都排一下序,排序的規則是先按起始邊界遞增,起始邊界相同的,按照結束邊界遞增。實現的時候用傳遞一個比較函數做參數的庫函數就能輕鬆搞定。排好序之後,從左往右掃,一開始遇到的那一段的起始,一定是新段的起始,然後如果當前的結束位置比遇到的這一段的起始位置大,那說明這兩段相交了,結束位置應該延伸到這兩段最遠的那個端點,然後繼續往後找,直到當前段的結束位置比下一段的起始位置還要小,說明兩段完全不相交,那麼將當前段的結束位置更新為剛剛那個過程中所能延伸到的最遠位置,這就完成了一次合并。然後下一次迴圈時,從合并後的後一段開始。
代碼還是比較簡潔的,也很好理解:
bool compare(const Interval &a, const Interval &b){ if(a.start != b.start) return a.start<b.start; else return a.end<b.end;}class Solution {public: vector<Interval> merge(vector<Interval> &intervals) { int msize = intervals.size(); vector<Interval> res; if(msize == 0) return res; sort(intervals.begin(), intervals.end(), compare); Interval itv = intervals[0]; int i=0, tpmax=0; while(i<msize){ tpmax = itv.end; while(i<msize&&tpmax>=intervals[i].start){ tpmax = max(tpmax, intervals[i].end); i++; } itv.end = tpmax; if(i==msize){ res.push_back(itv); break; } if(itv.end<intervals[i].start){ res.push_back(itv); itv = intervals[i]; } } return res; }};