题目描述

✅ 621. 任务调度器

image-20260928235800578

image-20260928235800579

题意分析

每个任务占一个时间单位,任务顺序可以重排。两次执行相同种类的任务之间,必须放下至少 n 个其他任务或待命位置,也就是两次执行的位置至少相差 n+1。求完成全部任务的最短时间,只需计算长度。

解法:按最高频任务构造时间框架

核心思路

[!blue]

设任务总数为 m,最大频次为 maxFreq,达到这个频次的种类数为 maxCount。总时间显然不能少于 m;重复最多的任务还会给出另一个下界。

在这 maxCount 种任务中,第一次执行最晚的那一种,首次出现不会早于第 maxCount 个时间单位。它还要再执行 maxFreq-1 次,每两次至少相隔 n+1,因此完成时间至少为 (maxFreq-1)*(n+1)+maxCount,记作 frame。

这两个下界的较大值可以通过轮次安排达到:前 maxFreq-1 轮各放一次所有最高频任务,最后一轮再按同样顺序各放一次。前面的轮次至少保留 n+1 个位置,最后一轮只有 maxCount 个任务,结束后不必继续冷却。

其余种类最多出现 maxFreq-1 次,可以分散到前面的轮次中。具体地,先分配需要占满每一轮的种类,再逐种把剩余任务按轮次循环填入,使各轮长度最多相差 1。同种任务在相邻轮的位置相同;若分配绕回第一轮,由于该种类没有占满所有轮,两段之间至少有一轮不含它,因此也能保持冷却间隔。

每轮不足 n+1 的部分用待命补足。任务少时,总长度正好是 frame;任务多到超过这些位置时,各轮都已填满,扩展后的间隔只会更长,无需待命,总长度就是 m。所以最优答案为 max(m, frame)。若 maxFreq=1,所有任务都不同,直接执行完即可,这个公式仍返回 m。

解题步骤

  • 用长度为 26 的数组统计每种大写字母的次数,同时求出 maxFreq。
  • 频次统计完成后,再扫描频次数组,计算有多少种任务达到 maxFreq,得到 maxCount。
  • 计算 frame = (maxFreq-1)*(n+1)+maxCount。
  • 返回 frame 与任务总数的较大者;不需要实际模拟上述轮次或保存任务顺序。

n=0 时相同任务可以连续执行,公式返回任务数;只有一种任务时,只能在相邻两次执行之间待命,公式正好保留这 maxFreq-1 段冷却时间。

代码实现

class Solution {
    public int leastInterval(char[] tasks, int n) {
        int[] freq = new int[26];
        int maxFreq = 0;

        for (char task : tasks) {
            freq[task - 'A']++;
            maxFreq = Math.max(maxFreq, freq[task - 'A']);
        }

        // 统计最终最大频次对应的任务种类数
        int maxCount = 0;

        for (int count : freq) {
            if (count == maxFreq) {
                maxCount++;
            }
        }

        // 最后一轮只放并列最多的任务,不再补尾部冷却
        int frame = (maxFreq - 1) * (n + 1) + maxCount;

        return Math.max(frame, tasks.length);
    }
}
func leastInterval(tasks []byte, n int) int {
    freq := make([]int, 26)
    maxFreq := 0
    for _, task := range tasks {
        idx := int(task - 'A')
        freq[idx]++
        if freq[idx] > maxFreq {
            maxFreq = freq[idx]
        }
    }

    // 统计最终最大频次对应的任务种类数
    maxCount := 0
    for _, count := range freq {
        if count == maxFreq {
            maxCount++
        }
    }

    // 最后一轮只放并列最多的任务,不再补尾部冷却
    frame := (maxFreq-1)*(n+1) + maxCount
    if frame > len(tasks) {
        return frame
    }
    return len(tasks)
}

复杂度分析

  • 时间复杂度:$O(m)$,m 为任务总数,字母表扫描为常数。
  • 空间复杂度:$O(1)$,固定频次表。

关键点总结

[!green]

  • maxCount 是并列最多的种类数,不是任务总次数。
  • n 是中间必须隔开的数量,两次执行位置的差应为 n+1。
  • 最高频任务决定必要的轮次数,其他任务优先占用本来需要待命的位置。
  • 两个下界都必须满足,最后一轮之后不再计算冷却时间。

易错点总结

[!yellow]

  • 只返回框架长度,任务过多时可能少于实际任务数。
  • 框架使用 maxFreq 轮完整间隔,会多算末尾待命。
  • 最大频次改变后仍累计旧的并列数量,会算错尾轮规模。

相似题目

题目 难度 关联与区别
358. K 距离间隔重排字符串 困难 同样限制相同任务或字符间的距离,原题构造字符串且不允许空闲,本题可插入空闲并最小化总长度。
767. 重构字符串 中等 相同字符不能相邻是冷却间隔为1的相近约束,本题还允许等待时隙。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/36140983
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!