[LeetCode] Task Scheduler 任務行程表

 

Given a char array representing tasks CPU need to do. It contains capital letters A to Z where different letters represent different tasks.Tasks could be done without original order. Each task could be done in one interval. For each interval, CPU could finish one task or just be idle.html

However, there is a non-negative cooling interval n that means between two same tasks, there must be at least n intervals that CPU are doing different tasks or just be idle.java

You need to return the least number of intervals the CPU will take to finish all the given tasks.api

Example 1:數組

Input: tasks = ['A','A','A','B','B','B'], n = 2
Output: 8
Explanation: A -> B -> idle -> A -> B -> idle -> A -> B.

 

Note:post

  1. The number of tasks is in the range [1, 10000].
  2. The integer n is in the range [0, 100].

 

這道題讓咱們安排CPU的任務,規定在兩個相同任務之間至少隔n個時間點。說實話,剛開始博主並無徹底理解題目的意思,後來看了大神們的解法才悟出個道理來。下面這種解法參考了大神fatalme的帖子,因爲題目中規定了兩個相同任務之間至少隔n個時間點,那麼咱們首先應該處理的出現次數最多的那個任務,先肯定好這些高頻任務,而後再來安排那些低頻任務。若是任務F的出現頻率最高,爲k次,那麼咱們用n個空位將每兩個F分隔開,而後咱們按順序加入其餘低頻的任務,來看一個例子:url

AAAABBBEEFFGG 3spa

咱們發現任務A出現了4次,頻率最高,因而咱們在每一個A中間加入三個空位,以下:code

A---A---A---Ahtm

AB--AB--AB--A   (加入B)blog

ABE-ABE-AB--A   (加入E)

ABEFABE-ABF-A   (加入F,每次儘量填滿或者是均勻填充)

ABEFABEGABFGA   (加入G)

再來看一個例子:

ACCCEEE 2

咱們發現任務C和E都出現了三次,那麼咱們就將CE看做一個總體,在中間加入一個位置便可:

CE-CE-CE

CEACE-CE   (加入A)

注意最後面那個idle不能省略,否則就不知足相同兩個任務之間要隔2個時間點了。

這道題好在沒有讓咱們輸出任務安排結果,而只是問所需的時間總長,那麼咱們就想個方法來快速計算出所需時間總長便可。咱們仔細觀察上面兩個例子能夠發現,都分紅了(mx - 1)塊,再加上最後面的字母,其中mx爲最大出現次數。好比例子1中,A出現了4次,因此有A---模塊出現了3次,再加上最後的A,每一個模塊的長度爲4。例子2中,CE-出現了2次,再加上最後的CE,每一個模塊長度爲3。咱們能夠發現,模塊的次數爲任務最大次數減1,模塊的長度爲n+1,最後加上的字母個數爲出現次數最多的任務,可能有多個並列。這樣三個部分都搞清楚了,寫起來就不難了,咱們統計每一個大寫字母出現的次數,而後排序,這樣出現次數最多的字母就到了末尾,而後咱們向前遍歷,找出出現次數同樣多的任務個數,就能夠迅速求出總時間長了,下面這段代碼可能最很差理解的可能就是最後一句了,那麼咱們特別來說解一下。先看括號中的第二部分,前面分析說了mx是出現的最大次數,mx-1是能夠分爲的塊數,n+1是每塊中的個數,然後面的 25-i 是還須要補全的個數,用以前的例子來講明:

AAAABBBEEFFGG 3

A出現了4次,最多,mx=4,那麼能夠分爲mx-1=3塊,以下:

A---A---A---

每塊有n+1=4個,最後還要加上末尾的一個A,也就是25-24=1個任務,最終結果爲13:

ABEFABEGABFGA

再來看另外一個例子:

ACCCEEE 2

C和E都出現了3次,最多,mx=3,那麼能夠分爲mx-1=2塊,以下:

CE-CE-

每塊有n+1=3個,最後還要加上末尾的一個CE,也就是25-23=2個任務,最終結果爲8:

CEACE-CE

好,那麼此時你可能會有疑問,爲啥還要跟原任務個數len相比,取較大值呢?咱們再來看一個例子:

AAABBB 0

A和B都出現了3次,最多,mx=3,那麼能夠分爲mx-1=2塊,以下:

ABAB

每塊有n+1=1個?你會發現有問題,這裏明明每塊有兩個啊,爲啥這裏算出來n+1=1呢,由於給的n=0,這有沒有矛盾呢,沒有!由於n表示相同的任務間須要間隔的個數,那麼既然這裏爲0了,說明相同的任務能夠放在一塊兒,這裏就沒有任何限制了,咱們只須要執行完全部的任務就能夠了,因此咱們最終的返回結果必定不能小於任務的總個數len的,這就是要對比取較大值的緣由了。

參見代碼以下:

 

解法一:

class Solution {
public:
    int leastInterval(vector<char>& tasks, int n) {
        vector<int> cnt(26, 0);
        for (char task : tasks) {
            ++cnt[task - 'A'];
        }
        sort(cnt.begin(), cnt.end());
        int i = 25, mx = cnt[25], len = tasks.size();
        while (i >= 0 && cnt[i] == mx) --i;
        return max(len, (mx - 1) * (n + 1) + 25 - i);
    }
};

 

下面這種解法是根據大神jinzhou的帖子,優勢是代碼更容易讀懂,並且變量命名很reasonable,前半部分都是同樣的,求出最多的次數mx,還有同時出現mx次的不一樣任務的個數mxCnt。這個解法的思想是先算出全部空出來的位置,而後計算出全部須要填入的task的個數,若是超出了空位的個數,就須要最後再補上相應的個數。注意這裏若是有多個任務出現次數相同,那麼將其總體放一塊兒,就像上面的第二個例子中的CE同樣,那麼此時每一個part中的空位個數就是n - (mxCnt - 1),那麼空位的總數就是part的總數乘以每一個part中空位的個數了,那麼咱們此時除去已經放入part中的,還剩下的task的個數就是task的總個數減去mx * mxCnt,而後此時和以前求出的空位數相比較,若是空位數要大於剩餘的task數,那麼則說明還需補充多餘的空位,不然就直接返回task的總數便可,參見代碼以下:

 

解法二:

class Solution {
public:
    int leastInterval(vector<char>& tasks, int n) {
        int mx = 0, mxCnt = 0;
        vector<int> cnt(26, 0);        
        for (char task : tasks) {
            ++cnt[task - 'A'];
            if (mx == cnt[task - 'A']) {
                ++mxCnt;
            } else if (mx < cnt[task - 'A']) {
                mx = cnt[task - 'A'];
                mxCnt = 1;
            }
        }
        int partCnt = mx - 1;
        int partLen = n - (mxCnt - 1);
        int emptySlots = partCnt * partLen;
        int taskLeft = tasks.size() - mx * mxCnt;
        int idles = max(0, emptySlots - taskLeft);
        return tasks.size() + idles;
    }
};

 

下面這種解法是參考的大神alexander的解法,思路是創建一個優先隊列,而後把統計好的個數都存入優先隊列中,那麼大的次數會在隊列的前面。這題仍是要分塊,每塊能裝n+1個任務,裝任務是從優先隊列中取,每一個任務取一個,裝到一個臨時數組中,而後遍歷取出的任務,對於每一個任務,將其哈希表映射的次數減1,若是減1後,次數仍大於0,則將此任務次數再次排入隊列中,遍歷完後若是隊列不爲空,說明該塊所有被填滿,則結果加上n+1。咱們以前在隊列中取任務是用個變量cnt來記錄取出任務的個數,咱們想取出n+1個,若是隊列中任務數少於n+1個,那就用cnt來記錄真實取出的個數,當隊列爲空時,就加上cnt的個數,參見代碼以下:

 

解法三:

class Solution {
public:
    int leastInterval(vector<char>& tasks, int n) {
        int res = 0, cycle = n + 1;
        unordered_map<char, int> m;
        priority_queue<int> q;
        for (char c : tasks) ++m[c];
        for (auto a : m) q.push(a.second);
        while (!q.empty()) {
            int cnt = 0;
            vector<int> t;
            for (int i = 0; i < cycle; ++i) {
                if (!q.empty()) {
                    t.push_back(q.top()); q.pop();
                    ++cnt;
                }
            }
            for (int d : t) {
                if (--d > 0) q.push(d);
            }
            res += q.empty() ? cnt : cycle;
        }
        return res;
    }
};

 

相似題目:

Rearrange String k Distance Apart

Reorganize String

 

參考資料:

https://leetcode.com/problems/task-scheduler/

https://leetcode.com/problems/task-scheduler/discuss/104493/c-java-clean-code-priority-queue

https://leetcode.com/problems/task-scheduler/discuss/104496/concise-java-solution-on-time-o26-space

https://leetcode.com/problems/task-scheduler/discuss/104500/java-on-time-o1-space-1-pass-no-sorting-solution-with-detailed-explanation

 

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

相關文章
相關標籤/搜索