[路飞]_每天刷leetcode_17(任务调度器 Task Scheduler)

496 阅读2分钟

任务调度器

LeetCode传送门621. Task Scheduler

题目

给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。

然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。

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

Given a characters array tasks, representing the tasks a CPU needs to do, where each letter represents a different task. Tasks could be done in any order. Each task is done in one unit of time. For each unit of time, the CPU could complete either one task or just be idle.

However, there is a non-negative integer n that represents the cooldown period between two same tasks (the same letter in the array), that is that there must be at least n units of time between any two same tasks.

Return the least number of units of times that the CPU will take to finish all the given tasks.

Exampe:

Input: tasks = ["A","A","A","B","B","B"], n = 2
Output: 8
Explanation: 
A -> B -> idle -> A -> B -> idle -> A -> B
There is at least 2 units of time between any two same tasks.

Input: tasks = ["A","A","A","B","B","B"], n = 0
Output: 6
Explanation: On this case any permutation of size 6 would work since n = 0.
["A","A","A","B","B","B"]
["A","B","A","B","A","B"]
["B","B","B","A","A","A"]
...
And so on.

Input: tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2
Output: 16
Explanation: 
One possible solution is
A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> idle -> idle -> A -> idle -> idle -> A

Constraints:

  • 1 <= task.length <= 10^4
  • tasks[i] is upper-case English letter.
  • The integer n is in the range [0, 100].

思考线


解题思路

做这道题我们首先也是要找一下如何排序才能让执行任务所需的世界最短。

我的想法是,首先计算出一共有多少个种类放到一个数组中tasksCounts,如果种类大于 n,则先排列最多的那个,然后排列次多的,...依次类推。一直排到次多的不够了,我们用待命来填充,直到 tasksCouns为空了,我们也就计算出了最短的时间。

现在问题来了,如何来确定排序的顺序呢?

在数据结构上,我们采用二维数组来保存每个任务的冷却时间和次数。tasksCounts中的每个任务用一个数组来表示[task, nextRunTime, counts]

  • task代表任务名称
  • nextRunTime 代表该任务的下次最小可执行时间
  • counts代表还有多少该任务需要执行

最后我们用 times来模拟时间的流逝,设置最初的值为0, 所有 nextRunTime的默认值也是0.

最后的最后我们再来一个优化点,我们可以在每次计算的时候记录一下下次最小的可执行时间,最后在判断的时候如果 minRunTime 大于 times,则需要待命来填充。由于每次times的增加都需要大量的计时,我们可以在可能出现待命的情况下,直接 让 times = minRunTime来加速时间的流逝,减少不必要的代码执行,从而达到优化效果。

根据上面的思路我们设计如下代码

/**
 * @param {character[]} tasks
 * @param {number} n
 * @return {number}
 */
var leastInterval = function(tasks, n) {
    // 计算出一共有多少个种类,如果种类大于n, 则先排列 最多的那个 m 次多,次次多,。。。n,m,。。。。m.
    // 一直到次多的不够了,用 待命来填充.或者全部填充完毕,计算出一共的时长
    // 在数据结构上 我们采用二维数组来保存每个任务的冷却时间和次数 [task, nextRunTime, counts]
    // 我们用time来模拟时间的流逝, 设最初的时间为0, nextRunTime也为0;
    let times = 0;
    let minRunTime = Number.MAX_SAFE_INTEGER;
    const tasksCounts = [];
    tasks.forEach( task => {
        const i = tasksCounts.findIndex( item => task === item[0]);
        if(i === -1) {
            tasksCounts.push([task, 0, 1])
        } else {
            tasksCounts[i][2]++;
        }
    });
    while(tasksCounts.length) {
        // 找出nextRunTime <= tims最大的counts,同时记录一下最小的nextRunTime,以便不用执行待命时间
        let run;
        let runIndex;
        tasksCounts.forEach( (item, i) => {
            if(item[1] <=times && (!run || run[2] < item[2])) {
                run = item;
                runIndex = i;
            }
            if(minRunTime > item[1]) minRunTime = item[1]
        })
        if(run) {
            run[1] += n +1;
            if(minRunTime > run[1]) minRunTime = run[1]
            if(--run[2] <=0) {
                tasksCounts.splice(runIndex, 1);
            }
        }
        times ++;
        if(minRunTime >= times) {
            times = minRunTime;
        }
    }

    return times;
};

这就是我对本题的解法,如果有疑问或者更好的解答方式,欢迎留言互动。