一日一练: 任务调度器

201 阅读3分钟

给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表,在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。然而,两个 相同种类 的任务之间必须有长度为整数n的冷却时间

这道题可能需要注意的地方:

    1. 根据冷却时间,有两种情况
    • 任务类型大于等于n,只需要分配n个不同的任务就可以
    • 任务类型小于n,分配完任务类型之后,需要用待机去填充冷却时间
    1. 为了保证尽可能多的执行任务,每次都先取剩余次数最多的任务执行

下面是解题思路:

tasks = ["A","A","A","B","B","B"] n = 2 为例

    1. 统计每个任务出现的次数,["A","A","A","B","B","B"]的统计结果为{"A": 3, "B": 3},映射成剩余任务次数数组rest [3, 3]
    1. 建立有效数组nextValid,来统计当前任务是否可以调度。

    这里是否可以调度的含义,还是以实例说明:这里的冷却时间为2,如果先执行A,那么下一个可以是B,接下里就只能待命,如下图

    时刻: 1   2   3   4   5    6   7   ...
    队列: A   B  待命  A   B   待命  A   ...
    

    最开始可以执行A或者B,先将nextValid赋值为[1, 1],这里nextValidindex与剩余次数统计数组restindex相对应

    1. 开始执行任务,当前时刻为1,只要nextValid小于等于这个值,即可以执行对应任务

    • a. 首先查找有效值最小(表明最久没执行)且剩余次数不为0的任务,例子中开始这里都是1,两个都符合,时刻1有可以执行的任务
    • b. 然后用1去查找属于次数最多的任务,这里都是3次,取任务A执行
    • c. 修改任务剩余次数:[2, 3],同时修改AnextValid的值(当前时刻 + 冷却时间 + 1)[4, 1]
    • d. 执行下一个任务,时刻为2
    • e. 在nextValid中寻找等于小于2的值,得到1,说明时刻2有可以执行的任务
    • f. 然后查找nextValid小于时刻2,且次数最多的任务index,结果为1,执行任务B
    • g. 然后修改任务B次数,rest[2, 2],修改BnextValid的值(当前时刻 + 冷却时间 + 1):[4, 2 + 2 + 1]
    • h. 下一个时刻为3,查找在nextValid查找最小值,4,所以时刻3没有任务可以执行,待命状态,同时将时刻改成4
    • i. 在nextValid中查找小于等于4且数量最大的任务的索引0,执行0对应的任务A
    • j. 修改任务次数 [1, 2],以及nextValid的值:[4 + 2 +1, 5]
    • ...重复上述步骤
    1. 返回最后的执行任务的时刻
function leastInterval(tasks: string[], n: number): number {
    if (n === 0) return tasks.length
    const obj = {}
    // 统计任务数量
    for(let i = 0; i < tasks.length; i++) {    
        obj[tasks[i]] = (obj[tasks[i]] || 0) + 1
    }
    const taskKindLength = Object.keys(obj).length
    const nextValid = new Array(taskKindLength).fill(1)
    // 剩余数量数组
    const rest: number[] = Object.values(obj)
    // 执行任务的时刻
    let times = 0
    for(let i = 0; i < tasks.length; i++) {
        times++
        // 找 距离可用的任务
        let minNextValid = Number.MAX_VALUE
        for(let i = 0; i < taskKindLength; i++) {
            // 保证还有 该任务
            if (rest[i] > 0) {
                minNextValid = Math.min(minNextValid, nextValid[i])
            }
        }
        times = Math.max(times, minNextValid)        
        let best = -1
        for(let j = 0; j < taskKindLength; j++) {
            // 保证任务是符合冷却时间的
            if (rest[j] && nextValid[j] <= times) {
                // 找次数最多的任务
                if (best === -1 || rest[j] > rest[best]) {
                    best = j
                }
            }            
        }
        nextValid[best] = times + n + 1
        rest[best]--
    }
    return times
}