问题:
给定一个用字符数组表示的 CPU 需要执行的任务列表。使用 A - Z 表示的26 种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。CPU 在任何一个单位时间内都可以执行一个任务,或者在待命状态。
然而,两个相同种类的任务之间必须有长度为 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。
你需要计算完成所有任务所需要的最短时间。
示例 :
输入:tasks = [“A”,“A”,“A”,“B”,“B”,“B”],n = 2
输出:8
解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B.
分析:
两个相同的任务之间必须有长度为 n 的冷却时间,我们可以将 n+1 个任务看作 CPU 的一轮执行,并且在一轮执行中,n+1 个任务必须是不同的,才能保证相同任务之间至少有 n 的冷却时间。
因为每个任务都有 n 的冷却时间,我们可以让任务数量最多的那个任务首先开始执行,这样可以减少空闲时间。
例:
tasks = [“A”,“A”,“B”,“C”],n = 2
如果 “B” 先执行,执行结果 “B -> A -> C -> 空闲 -> A”
如果 “A” 先执行,执行结果 “A -> B -> C -> A”
我们需要对任务进行统计,并按照任务数量的多少进行排序,从数量最多的任务开始执行,依次执行 n+1 个任务,每次执行一个任务,对应的任务数量需要减一,当没有任务可以执行时,用空闲时间代替。执行完 n+1 个任务后,按照任务数量重新排序,继续执行 n+1 个任务,直到任务全部执行完毕。
代码如下:
public int leastInterval(char[] tasks, int n) {
int map[] = new int[26];
// 对任务个数进行统计
for(char t : tasks)
map[t-'A'] += 1;
Arrays.sort(map); // 对任务进行升序排序
int times = 0;
while(map[25] > 0) {
int i = 0; // i 表示一轮任务中已执行的任务数量
while(i <= n) {
// 当任务数量最多的那个任务为 0 时,意味着这是最后一轮任务
// 此时无需考虑冷却时间(只需要将剩下未执行完的任务执行即可)
if(map[25] == 0)
break;
// 如果存在可以执行的任务,执行该任务,并将该任务数量减一
// 如果不存在可执行的任务,times直接加一,表示空闲时间
if(i < 26 && map[25-i] > 0)
map[25-i]--;
// times 表示执行任务消耗的秒数
times++;
i++;
}
Arrays.sort(map);
}
return times;
}
假设现有任务 { “A”,“A”,“A” },冷却时间为 3,那么 CPU 执行完该过程需要的时间 times = 2 x (3 + 1) + 1,执行前两个 “A” 需要冷却时间 3 ,执行最后一个 “A”,不需要考虑冷却时间。
那么,假设任务 “A” 的个数最多,且个数为 n ,冷却时间为 p 时,CPU 执行需要的时间 times = (n - 1) x (p + 1) + 1。
此时的空闲时间为 (n - 1) x p,我们可以将其他的任务填补到空闲时间中。当其他任务全部填补到空闲时间,还有剩余的空闲时间,CPU 的执行时间 times = 剩余空闲时间 + 总任务数;当空闲时间全部被填补,还有剩余任务时,CPU 的执行时间为总任务数。
A | B | C | D |
---|---|---|---|
A | B | C | D |
A | B | C | |
A | B | D |
上图的空闲时间没有被填满,因此,CPU 的执行时间 times = 14 + 1 = 15(剩余空闲时间 = 1,总任务数 = 14)。
代码如下:
public int leastInterval(char[] tasks, int n) {
int map[] = new int[26];
for(char t : tasks)
map[t-'A'] += 1;
Arrays.sort(map);
int max = map[25] - 1;
// freeTime表示空闲时间
int freeTime = max * n;
for(int i = 0; i < 25; i++) {
freeTime -= Math.min(map[i],max);
}
if(freeTime > 0)
return freeTime + tasks.length;
else
return tasks.length;
}