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
这道题让咱们安排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
参考资料:
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