Medium

题目描述

给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表,用字母 A 到 Z 表示,以及一个冷却时间 n。每个 CPU 时间片可以完成一个任务,或者处于待命状态。

相同类型的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个时间片用来执行其他任务,或者处于待命状态。

你需要计算完成所有任务所需要的最短时间

示例 1:

输入:tasks = ["A","A","A","B","B","B"], n = 2
输出:8
解释:A -> B -> idle -> A -> B -> idle -> A -> B
     在完成任务 A 之后,你必须等待两个间隔。对任务 B 同样如此。
     在第 3 个间隔,A 和 B 都不能完成,所以你需要待命。
     在第 4 个间隔,你可以再次开始任务 A,因为它已经等待了 2 个间隔。

示例 2:

输入:tasks = ["A","C","A","B","D","B"], n = 1
输出:6
解释:一种可能的序列是:A -> B -> C -> D -> A -> B
     由于冷却间隔为 1,你可以在一个其他任务后重复一个任务。

示例 3:

输入:tasks = ["A","A","A", "B","B","B"], n = 3
输出:10
解释:一种可能的序列是:A -> B -> idle -> idle -> A -> B -> idle -> idle -> A -> B
     只有两种任务类型,A 和 B,它们需要被 3 个间隔分开。这导致在这些任务的重复之间有两次待命。

提示:

  • 1 <= tasks.length <= 10^4
  • tasks[i] 是大写英文字母
  • 0 <= n <= 100

解题思路

解题思路

这道题有两种主要思路:

思路一:数学公式法(推荐)

观察规律可以发现,最终时间主要由出现次数最多的任务决定。设最高频率为 maxCount,有 countOfMax 个任务达到这个频率。

核心观察:

  • 出现次数最多的任务之间必须间隔 n 个时间片
  • 可以将时间轴按照最高频任务分割成若干块,每块长度为 n+1
  • 最后一块可能不满 n+1,只需要放置剩余的最高频任务

计算公式:max(tasks.length, (maxCount-1) * (n+1) + countOfMax)

思路二:优先队列模拟法

使用最大堆维护任务频率,每次取出频率最高的可执行任务。通过队列记录冷却中的任务,时间到了再重新加入堆中。这种方法更直观但时间复杂度较高。

数学公式法更优雅高效,是首选解法。当任务种类很多且分布均匀时,不需要等待,直接返回任务总数;当某些任务频率很高时,需要考虑冷却等待时间。

代码实现

class Solution {
public:
    int leastInterval(vector<char>& tasks, int n) {
        unordered_map<char, int> freq;
        for (char task : tasks) {
            freq[task]++;
        }
        
        int maxCount = 0;
        for (auto& p : freq) {
            maxCount = max(maxCount, p.second);
        }
        
        int countOfMax = 0;
        for (auto& p : freq) {
            if (p.second == maxCount) {
                countOfMax++;
            }
        }
        
        return max((int)tasks.size(), (maxCount - 1) * (n + 1) + countOfMax);
    }
};
class Solution:
    def leastInterval(self, tasks: List[str], n: int) -> int:
        from collections import Counter
        
        freq = Counter(tasks)
        max_count = max(freq.values())
        count_of_max = sum(1 for count in freq.values() if count == max_count)
        
        return max(len(tasks), (max_count - 1) * (n + 1) + count_of_max)
public class Solution {
    public int LeastInterval(char[] tasks, int n) {
        var freq = new Dictionary<char, int>();
        foreach (char task in tasks) {
            freq[task] = freq.GetValueOrDefault(task, 0) + 1;
        }
        
        int maxCount = freq.Values.Max();
        int countOfMax = freq.Values.Count(count => count == maxCount);
        
        return Math.Max(tasks.Length, (maxCount - 1) * (n + 1) + countOfMax);
    }
}
/**
 * @param {character[]} tasks
 * @param {number} n
 * @return {number}
 */
var leastInterval = function(tasks, n) {
    const freq = new Map();
    let maxFreq = 0;
    
    for (const task of tasks) {
        freq.set(task, (freq.get(task) || 0) + 1);
        maxFreq = Math.max(maxFreq, freq.get(task));
    }
    
    let maxFreqCount = 0;
    for (const count of freq.values()) {
        if (count === maxFreq) {
            maxFreqCount++;
        }
    }
    
    const minIntervals = (maxFreq - 1) * (n + 1) + maxFreqCount;
    
    return Math.max(minIntervals, tasks.length);
};

复杂度分析

复杂度类型数学公式法
时间复杂度O(m),其中 m 是任务种类数,最多26种
空间复杂度O(m),用于存储任务频率

相关题目