[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优先队列。思路跟358767类似,你需要为相同字母隔开一定的距离,且首先需要保证为出现次数最多的字母隔开足够的距离。满足这个大前提之后,中间的空格就可以塞入其他字符了。这个题的思路也是类似,需要注意的细节是,如果出现次数最多的字母出现了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 621: Task Scheduler

[leetcode]Queue-621. Task Scheduler

leetcode621 - Task Scheduler - medium

[leetcode]621. Task Scheduler任务调度

[LeetCode] 621. Task Scheduler

621. Task Scheduler