[LeetCode] 621. Task Scheduler
Posted aaronliu1991
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LeetCode] 621. Task Scheduler相关的知识,希望对你有一定的参考价值。
任务调度器。给定一个用字符数组表示的 CPU 需要执行的任务列表。其中包含使用大写的 A - Z 字母表示的26 种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。CPU 在任何一个单位时间内都可以执行一个任务,或者在待命状态。然而,两个相同种类的任务之间必须有长度为 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。你需要计算完成所有任务所需要的最短时间。例子,
Example:
Input: tasks = ["A","A","A","B","B","B"], n = 2 Output: 8 Explanation: A -> B -> idle -> A -> B -> idle -> A -> B.
这个题我给出两种解法,都是利用贪心的思路,一种是数学解法,一种会用到priority queue优先队列。思路跟358,767类似,你需要为相同字母隔开一定的距离,且首先需要保证为出现次数最多的字母隔开足够的距离。满足这个大前提之后,中间的空格就可以塞入其他字符了。这个题的思路也是类似,需要注意的细节是,如果出现次数最多的字母出现了max次,冷却时间为n的话,你需要为整个输出预留(max - 1) * (n + 1)。这个结论我第一次看的时候不理解, 后来看了一个例子记住了。
AAAABBBEEFFGG N = 3
摆放结果
任务A出现了4次,频率最高,于是我们在每个A中间加入三个空位,如下:
A---A---A---A
AB--AB--AB--A (加入B)
ABE-ABE-AB--A (加入E)
ABEFABE-ABF-A (加入F,每次尽可能填满或者是均匀填充)
ABEFABEGABFGA (加入G)
如上这个例子,max应该是A出现的次数 = 4,所以预留长度应该是间隔的出现次数 * 每个间隔长度,(4 - 1) * (3 + 1) = 3 * 4 = 12。为什么呢?因为A出现了4次,4个A之间会需要三个间隔,所以次数是3;每个间隔的长度是冷却时间 + 1,比如之前这个例子,冷却时间是2,但是每个A之间的间隔是3。
Example:
Input: tasks = ["A","A","A","B","B","B"], n = 2 Output: 8 Explanation: A -> B -> idle -> A -> B -> idle -> A -> B.
至于具体的做法,是先用hashmap统计每个出现的字母及其次数,然后找出出现次数最大的字母的次数,先依据这个最大次数和冷却时间,算出一个符合条件的最短距离。同时注意两个corner case
1. 如果出现次数最多的字母有多个,那么最后需要再加上;
2. 会存在不需要加额外冷却时间的情形,比如这样,tasks = ["A","A","A","B","B","B"], n = 1,只要把A和B隔开排列就行了。
时间O(n)
空间O(1)
Java实现
1 class Solution { 2 public int leastInterval(char[] tasks, int n) { 3 char[] cnt = new char[26]; 4 int maxn = 0; 5 for (int task : tasks) { 6 cnt[task - ‘A‘]++; 7 maxn = Math.max(maxn, cnt[task - ‘A‘]); 8 } 9 int ans = (maxn - 1) * (n + 1); 10 for (int i = 0; i < 26; i++) { 11 if (cnt[i] == maxn) { 12 ans++; 13 } 14 } 15 return Math.max(ans, tasks.length); 16 } 17 }
再来是priority queue的做法。首先用pq建立一个最大堆,记录存每个字母的出现次数,出现次数最多的字母会在堆顶。再创建一个cycle变量,长度是n + 1,这是每个间隔的长度。接着开始pop字符,每pop出一个字符的时候,将他加入一个临时的list,因为有可能还会再加回pq。每次如果在cycle跑完之前,pq就遍历完了,则加入worktime,如果pq没有遍历完,则加入pq的size。
时间O(nlogn)
空间O(n)
Java实现
1 class Solution { 2 public int leastInterval(char[] tasks, int n) { 3 HashMap<Character, Integer> counts = new HashMap<>(); 4 for (char t : tasks) { 5 counts.put(t, counts.getOrDefault(t, 0) + 1); 6 } 7 PriorityQueue<Integer> queue = new PriorityQueue<>((a, b) -> b - a); 8 queue.addAll(counts.values()); 9 10 int res = 0; 11 int cycle = n + 1; 12 while (!queue.isEmpty()) { 13 int worktime = 0; 14 List<Integer> temp = new ArrayList<>(); 15 for (int i = 0; i < cycle; i++) { 16 if (!queue.isEmpty()) { 17 temp.add(queue.poll()); 18 worktime++; 19 } 20 } 21 for (int count : temp) { 22 if (--count > 0) { 23 queue.offer(count); 24 } 25 } 26 res += !queue.isEmpty() ? cycle : worktime; 27 } 28 return res; 29 } 30 }
以上是关于[LeetCode] 621. Task Scheduler的主要内容,如果未能解决你的问题,请参考以下文章
[leetcode]Queue-621. Task Scheduler
leetcode621 - Task Scheduler - medium
[leetcode]621. Task Scheduler任务调度