[LeetCode] Employee Free Time 職員的空閑時間


 

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:

  1. schedule and schedule[i] are lists with lengths in range [1, 50].
  2. 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;
    }
};

 

類似題目:

Merge Intervals

 

參考資料:

https://leetcode.com/problems/employee-free-time/discuss/113127/C++-Clean-Code

https://leetcode.com/problems/employee-free-time/discuss/113134/Simple-Java-Sort-Solution-Using-(Priority-Queue)-or-Just-ArrayList

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM