LeetCode 621任务调度器算法疑问:剩余任务冷却期处理逻辑
给定字符数组tasks表示CPU需执行的任务,每个字母代表不同任务,任务可按任意顺序执行,每个任务耗时1单位时间,每单位时间CPU可完成一个任务或处于空闲状态。存在非负整数n表示相同任务的冷却期,即相同任务之间至少间隔n单位时间。需返回CPU完成所有任务的最短时间。
def leastInterval(self, tasks: List[str], n: int) -> int: # 统计各任务的执行频率 frequencies = [0] * 26 for t in tasks: frequencies[ord(t) - ord('A')] += 1 frequencies.sort() # 获取最高频率的任务次数 f_max = frequencies.pop() idle_time = (f_max - 1) * n while frequencies and idle_time > 0: idle_time -= min(f_max - 1, frequencies.pop()) idle_time = max(0, idle_time) return idle_time + len(tasks)
该算法最终返回idle_time + len(tasks)。当所有任务可填满空闲时间时,返回len(tasks)容易理解,但如果空闲时间已被填满仍有剩余任务,为何len(tasks)能保证剩余任务可在满足冷却期的前提下处理?
例如输入为AAAABBBCC、冷却期n=1时,初始空闲位置为A_A_A_A(共3个空闲位),B的频率为3、C的频率为2。将3个B填入空闲位后得到ABABABA,剩余2个C,如何在满足冷却期1的条件下处理这两个C?
核心逻辑很简单:当空闲时间被填满后,剩余任务之所以能合法安排,是因为此时任务的种类或剩余任务的频率,足够让我们把它们插入到已排好的任务序列的“缝隙”里,完全不需要额外空闲时间。
拿你举的AAAABBBCC、n=1的例子来说:
按算法计算,f_max是4(A的次数),初始空闲时间是(4-1)*1=3。弹出B的频率3后,减去min(3,3)=3,空闲时间直接变为0;再弹出C的频率2时,空闲时间已经是0,最终返回0+9=9。
实际排序列的时候,不用局限于只填最初的空位。我们可以把C插入到序列的任意不违反冷却要求的位置,比如构造出ABABACBCA:
- 所有A之间的间隔分别是1、1、2,都满足≥1的冷却要求;
- B之间的间隔分别是1、2,也符合要求;
- C之间的间隔是2,同样满足条件。
整个序列没有空闲,总时长就是任务总数9,完全合法。
本质上,最短时间分两种情况:
- 冷却期限制了任务排布,必须留空闲:总时间=任务数+空闲时间
- 任务种类或数量足够多,能填满所有冷却间隔,不需要空闲:总时间=任务数
你的例子就属于第二种情况——当空闲时间被填满后,剩余任务的存在只会让序列的“密度”更高,而不会触发新的空闲需求,所以直接返回任务总数即可。
内容的提问来源于stack exchange,提问作者Mushahid Khan

