[LeetCode] 621. 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.

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.

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

Example:

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

Constraints:

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

任务调度器。给定一个用字符数组表示的 CPU 需要执行的任务列表。其中包含使用大写的 A - Z 字母表示的26 种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。CPU 在任何一个单位时间内都可以执行一个任务,或者在待命状态。然而,两个相同种类的任务之间必须有长度为 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。你需要计算完成所有任务所需要的最短时间。

这个题我给出两种解法,都是利用贪心的思路,一种是数学解法,一种会用到priority queue优先队列。思路跟358767类似,你需要为相同字母隔开一定的距离,且首先需要保证为出现次数最多的字母隔开足够的距离。满足这个大前提之后,中间的空格就可以塞入其他字符了。这个题的思路也是类似,需要注意的细节是,如果出现次数最多的字母出现了max次,冷却时间为n的话,你需要为整个输出预留(max - 1) * (n + 1) + 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次,其中3个A之间会需要三个间隔,所以次数是3,最后一个位置留给第四个A;每个间隔的长度是冷却时间 + 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. 会存在不需要加额外冷却时间的情形,比如这样,N小于tasks的长度,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 题目总结

原文地址:https://www.cnblogs.com/cnoodle/p/12778873.html