1. 题目
读题
https://leetcode.cn/problems/task-scheduler/
考查点
这道题的考查点是:
- 如何统计和排序任务的频率。
- 如何分析不同情况下的最少时间。
- 如何利用数学公式来简化计算。
2. 解法
思路
解法的思路是:
- 首先统计每种任务的出现频率,然后按照频率降序排序。
- 找出最高频率和具有最高频率的任务数量。
- 计算最少时间,有两种情况:
- 如果我们可以用其他任务填充空闲时间,那么最少时间就是任务总数。
- 如果我们不能用其他任务填充空闲时间,那么最少时间就是由最高频率的任务和空闲时间组成的框架长度乘以最高频率减一,再加上具有最高频率的任务数量。
代码的逻辑
代码的逻辑。代码分为三个部分:
-
第一部分是统计每种任务的出现频率,然后按照频率降序排序。这一部分使用了一个长度为26的整数数组freq,表示每个字母任务的频率。然后遍历tasks数组,对每个任务的频率加一。最后使用Arrays.sort方法对freq数组进行降序排序。
-
第二部分是找出最高频率和具有最高频率的任务数量。这一部分从freq数组的末尾开始遍历,因为末尾是最高频率的任务。用一个变量maxFreq记录最高频率,用一个变量maxCount记录具有最高频率的任务数量。如果遇到和maxFreq相同的频率,就把maxCount加一;如果遇到比maxFreq小的频率,就停止遍历。
-
第三部分是计算最少时间,有两种情况:
- 如果我们可以用其他任务填充空闲时间,那么最少时间就是任务总数。这种情况发生在n很小或者任务种类很多的时候,比如tasks = ["A","A","A","B","B","B"],n = 0,那么最少时间就是6。
- 如果我们不能用其他任务填充空闲时间,那么最少时间就是由最高频率的任务和空闲时间组成的框架长度乘以最高频率减一,再加上具有最高频率的任务数量。这种情况发生在n很大或者任务种类很少的时候,比如tasks = ["A","A","A","B","B","B"],n = 2,那么最少时间就是8。我们可以把这种情况想象成如下的图示:
A -> B -> idle -> A -> B -> idle -> A -> B
这里每个箭头表示一个单位时间,每个字母表示一个任务。我们可以看到,由于n = 2,所以相同任务之间必须有两个空闲时间。我们可以把这样的一个周期称为一个框架,比如上面的图示中有两个框架:
A -> B -> idle A -> B -> idle
每个框架的长度是n + 1 = 3,因为除了一个任务外还有n个空闲时间。每个框架中只能放置一个具有最高频率的任务,否则会违反冷却时间的限制。所以我们需要maxFreq - 1个框架来放置除了最后一个之外的所有具有最高频率的任务。比如上面的图示中,我们需要2 - 1 = 1个框架来放置除了最后一个A之外的所有A。
最后一个框架可能不完整,因为可能没有足够的其他任务来填充空闲时间。但是我们至少需要放置所有具有最高频率的任务,所以最后一个框架的长度至少是maxCount。比如上面的图示中,最后一个框架的长度是2,因为还有两个具有最高频率的任务B。
所以总共需要的时间就是(maxFreq - 1) * (n + 1) + maxCount。
具体实现
class Solution { public int leastInterval(char[] tasks, int n) { // count the frequency of each task int[] freq = new int[26]; for (char task : tasks) { freq[task - 'A']++; } // sort the frequencies in descending order Arrays.sort(freq); // find the max frequency and how many tasks have it int maxFreq = freq[25]; int maxCount = 0; for (int i = 25; i >= 0; i--) { if (freq[i] == maxFreq) { maxCount++; } else { break; } } // calculate the minimum time // either we fill up the idle slots with other tasks or we don't need any idle slots int minTime = Math.max(tasks.length, (maxFreq - 1) * (n + 1) + maxCount); return minTime; } }