任务调度器
题目
版本1 正确
public static int leastInterval(char[] tasks, int n) {
if (n == 0) {
return tasks.length;
}
// 任务种类 -> 剩余执行次数的映射
Map<Character, Integer> task2NumMap = new HashMap<>();
// 任务种类 -> 冷却时间的映射
Map<Character, Integer> task2TimeMap = new HashMap<>();
// 维护一个冷却时间为0的大顶堆
PriorityQueue<Character> priorityQueue = new PriorityQueue<>(new Comparator<Character>() {
@Override
public int compare(Character o1, Character o2) {
return task2NumMap.get(o2) - task2NumMap.get(o1);
}
});
int allTaskNum = 0;
for (char task : tasks) {
allTaskNum ++;
task2NumMap.put(task, task2NumMap.getOrDefault(task, 0) + 1);
task2TimeMap.putIfAbsent(task, 0);
}
// 一定要在所有次数都统计完毕后, 才构造优先队列, 才能正确的排序
task2NumMap.forEach((k, v) -> {
priorityQueue.offer(k);
});
// 每次选择任务依据几个准则
// (1) 如果都在冷却, 没得选, 等待一个cpu时间
// (2) 如果只有一个冷却好了的, 直接选
// (3) 如果有多个冷却好的任务, 选择剩余执行次数最大的那个
int ans = 0;
while (allTaskNum > 0) {
if (priorityQueue.size() == 0) {
// 等待一个cpu时间 没有执行任何任务
ans ++;
} else {
// 大顶堆顶部的元素就是应该执行的任务
Character executeTask = priorityQueue.poll();
allTaskNum --;
ans ++;
// 调整task2NumMap
int surplusNum = task2NumMap.get(executeTask) - 1;
task2NumMap.put(executeTask, surplusNum);
// 调整task2TimeMap, 这里调整为n + 1, 因为下面会统一减1
task2TimeMap.put(executeTask, n + 1);
}
// 每等待一个cpu时间, 或者说执行了某个任务后
// task2TimeMap中所有非0的值都应该减去1, 如果有减去到0的, 需要添加到priorityQueue中
task2TimeMap.forEach((k, time) -> {
// 注意冷却时间如果归零, 同时还要满足依旧有需要执行的次数, 才需要添加到优先队列中
if (time - 1 == 0 && task2NumMap.get(k) > 0) {
priorityQueue.offer(k);
}
if (time > 0) {
task2TimeMap.put(k, time - 1);
}
});
}
return ans;
}
正确的原因
(1) 注意优先队列赋值的情况, 需要等统计次数的map遍历完, 再进行赋值, 才能得到正确的排序
(2) 执行的过程中, 往优先队列添加值, 需要考虑是否有剩余次数了, 再添加