We are given a list schedule of employees, which represents the working time for each employee.
Each employee has a list of non-overlapping Intervals, and these intervals are in sorted order.
Return the list of finite intervals representing common, positive-length free time for all employees, also in sorted order.
Example 1:
Input: schedule = [[[1,2],[5,6]],[[1,3]],[[4,10]]] Output: [[3,4]] Explanation: There are a total of three employees, and all common free time intervals would be [-inf, 1], [3, 4], [10, inf]. We discard any intervals that contain inf as they aren't finite.
Example 2:
Input: schedule = [[[1,3],[6,7]],[[2,4]],[[2,5],[9,12]]] Output: [[5,6],[7,9]]
(Even though we are representing Intervals in the form [x, y], the objects inside are Intervals, not lists or arrays. For example, schedule[0][0].start = 1, schedule[0][0].end = 2, and schedule[0][0][0] is not defined.)
Also, we wouldn't include intervals like [5, 5] in our answer, as they have zero length.
Note:
scheduleandschedule[i]are lists with lengths in range[1, 50].0 <= schedule[i].start < schedule[i].end <= 10^8.
這道題和之前那道Merge Intervals基本沒有太大的區別,那道題是求合並后的區間,這道題求合並后區間中間不相連的區間。那么只要我們合並好了區間,就很容易做了。那么我么首先應該給所有的區間排個序,按照起始位置從小到大來排。因為我們總不可能一會處理前面的,一會處理后面的區間。排好序以后,我們先取出第一個區間賦給t,然后開始遍歷所有的區間內所有的區間,如果t的結束位置小於當前遍歷到的區間i的起始位置,說明二者沒有交集,那么把不相交的部分加入結果res中,然后把當前區間i賦值給t;否則如果區間t和區間i有交集,那么我們更新t的結束位置為二者中的較大值,因為按順序遍歷區間的時候,區間t的結束位置是比較的基准,越大越容易和后面的區間進行合並,參見代碼如下:
解法一:
class Solution { public: vector<Interval> employeeFreeTime(vector<vector<Interval>>& schedule) { vector<Interval> res, v; for (auto a : schedule) { v.insert(v.end(), a.begin(), a.end()); } sort(v.begin(), v.end(), [](Interval &a, Interval &b) {return a.start < b.start;}); Interval t = v[0]; for (Interval i : v) { if (t.end < i.start) { res.push_back(Interval(t.end, i.start)); t = i; } else { t = (t.end < i.end) ? i : t; } } return res; } };
我們再來看一種解法,這種解法挺巧妙的,我們使用TreeMap建立一個位置和其出現次數之間的映射,對於起始位置,進行正累加,對於結束位置,進行負累加。由於TreeMap具有自動排序的功能,所以我們進行遍歷的時候,就是從小到大進行遍歷的。定義一個變量cnt,初始化為0,我們對於每個遍歷到的數,都加上其在TreeMap中的映射值,即該數字出現的次數,起始位置的話就會加正數,結束位置就是加負數。開始的時候,第一個數字一定是個起始位置,那么cnt就是正數,那么接下來cnt就有可能加上正數,或者減去一個負數,我們想,如果第一個區間和第二個區間沒有交集的話,那么接下來遇到的數字就是第一個區間的結束位置,所以會減去1,這樣此時cnt就為0了,這說明一定會有中間區域存在,所以我們首先把第一個區間當前起始位置,結束位置暫時放上0,組成一個區間放到結果res中,這樣我們在遍歷到下一個區間的時候更新結果res中最后一個區間的結束位置。語言描述難免太干巴巴的,我們拿題目中的例1來說明,建立好的TreeMap如下所示:
1 -> 2
2 -> -1
3 -> -1
4 -> 1
5 -> 1
6 -> -1
10 -> -1
那么開始遍歷這所有的映射對,cnt首先為2,然后往后遍歷下一個映射對2 -> -1,此時cnt為1了,不進行其他操作,再往下遍歷,下一個映射對3 -> -1,此時cnt為0了,說明后面將會出現斷層了,我們將(3, 0)先存入結果res中。然后遍歷到4 -> 1時,cnt為1,此時將結果res中的(3, 0)更新為 (3, 4)。然后到5 -> 1,此時cnt為2,不進行其他操作,然后到6 -> -1,此時cnt為1,不進行其他操作,然后到10 -> -1,此時cnt為0,將(10, 0)加入結果res中。由於后面再沒有任何區間了,所以res最后一個區間不會再被更新了,我們應該將其移出結果res,因為題目中限定了區間不能為無窮,參見代碼如下:
解法二:
class Solution { public: vector<Interval> employeeFreeTime(vector<vector<Interval>>& schedule) { vector<Interval> res; map<int, int> m; int cnt = 0; for (auto employee : schedule) { for (Interval i : employee) { ++m[i.start]; --m[i.end]; } } for (auto a : m) { cnt += a.second; if (!cnt) res.push_back(Interval(a.first, 0)); if (cnt && !res.empty() && !res.back().end) res.back().end = a.first; } if (!res.empty()) res.pop_back(); return res; } };
類似題目:
參考資料:
https://leetcode.com/problems/employee-free-time/discuss/113127/C++-Clean-Code
