[LeetCode] 621. Task Scheduler

42 篇文章 0 订阅
37 篇文章 0 订阅

题目链接: https://leetcode.com/problems/task-scheduler/description/

Description

Given a char array representing tasks CPU need to do. It contains capital letters A to Z where different letters represent different tasks.Tasks could be done without original order. Each task could be done in one interval. For each interval, CPU could finish one task or just be idle.

However, there is a non-negative cooling interval n that means between two same tasks, there must be at least n intervals that CPU are doing different tasks or just be idle.

You need to return the least number of intervals the CPU will take to finish all the given tasks.

Example 1:

Input: tasks = ["A","A","A","B","B","B"], n = 2
Output: 8
Explanation: A -> B -> idle -> A -> B -> idle -> A -> B.

Note:

  1. The number of tasks is in the range [1, 10000].
  2. The integer n is in the range [0, 100].

解题思路

记 cooling interval 加 1 为一个调度周期,最优的顺序应该为在每个调度周期内,依据task数量,从多到少无重复依次执行,若无法填满整个调度周期,则剩下的空槽保留,开始下一轮调度,直到完成所有tasks

由于task的名字只可能为26个字母,并且结果与名字无关,因此用一个大小为26的数组存储每种task的数量,先统计出每种task的数量,用数量代替每种task来继续解题。

方法一:优先队列

大体思路为用优先队列模拟整个调度。声明一个权值大优先级高的优先队列,保证出队的顺序为从大到小。将每种task统计出的数量入队。并且,在每一轮中,用一个数组temp来记录已经出队且剩余数量大于 1 的task,在该轮结束后重新入队,以此来确保当轮调度无重复。

模拟每一轮调度,若队列不为空则出队,times加一,判断出队的task剩余数量若不为 0,则存入数组中,等待下一轮开始前重新入队;若队列为空,先将上一轮temp中的值重新入队,并且统计上一轮剩下的空槽数量加到times中,再开始下一轮调度。直到所有tasks都完成,即队列为空,返回times即为结果。

方法二:统计空闲槽

例子,tasks = ["A","A","A","B","B","B","C"], n = 2

task数最大值为max_val,空槽数最多为(max_val - 1) * n,记为idles

A--
A--
A

实际空槽数为最大空槽数减去其他task在除最后一轮以外的轮次中占用的槽数

ABC
AB-
AB

因此,这种方法可以先计算出最大空槽数idles = (max_val - 1) * n,然后对剩余的每一种task,用idles - min(task_size, max_val - 1)。另外,n 可能为 0,这样统计出的空槽数可能为负数,所以需要先判断空槽数是否大于 0,若大于则结果为空槽数加上tasks总数,否则为tasks总数。

方法三:优化版

与方法二类似,换了一种思路。不用统计空槽数,因为除了最后一轮以外的轮次所需时间肯定为n + 1,这些轮次总共需要时间为(max_val - 1) * (n + 1),重点在最后一个轮次。最后一轮中,只有数量等于max_valtask才会在该轮中调度,因此只需要统计出数量等于max_valtask数量,用前面轮次的总时间加上这个数量即为最终结果。

另外,还要考虑特殊情况,若 n 为 0,则上述方法统计出的结果会小于tasks总数。

举个例子,tasks = ["A","A","A","B","B","B"], n = 0

上述方法计算出的结果为 (3 - 1) * (0 + 1) + 2 = 4,而正确结果应该是tasks总数 6,因此最终结果应该为该方法算出的结果与tasks总数之间的较大值。

Code

方法一:优先队列

class Solution {
public:
    int leastInterval(vector<char>& tasks, int n) {
        int times = 0, i = n + 1;
        priority_queue<int, vector<int>, less<int>> pq;
        vector<int> alpha(26);

        for (char &ch: tasks) {
            alpha[ch - 'A']++;
        }

        for (int count: alpha) {
            if (count > 0) {
                pq.push(count);
            }
        }

        while (!pq.empty()) {
            times += n - i + 1;

            i = 0;
            list<int> temp;

            while (i <= n) {
                if (!pq.empty()) {
                    if (pq.top() > 1) {
                        temp.push_back(pq.top() - 1);
                    }
                    pq.pop();
                    times++;
                } else {
                    break;
                }

                i++;
            }

            for (auto count: temp) {
                pq.push(count);
            }
        }

        return times;
    }
};

方法二:统计空闲槽

class Solution {
public:
    int leastInterval(vector<char>& tasks, int n) {
        vector<int> alpha(26);

        for (char &ch: tasks) {
            alpha[ch - 'A']++;
        }

        sort(alpha.begin(), alpha.end());

        int max_val = alpha.back() - 1;
        int idles = max_val * n;

        for (int i = 24; i >= 0 && alpha[i] > 0; --i) {
            idles -= min(max_val, alpha[i]);
        }

        return idles > 0 ? idles + tasks.size() : tasks.size();
    }
};

方法三:优化版

class Solution {
public:
    int leastInterval(vector<char>& tasks, int n) {
        vector<int> alpha(26);
        for (char &ch: tasks) {
            alpha[ch - 'A']++;
        }
        sort(alpha.begin(), alpha.end());

        int i = 25, max_val = alpha.back(), len = tasks.size();
        while (i >= 0 && alpha[i] == max_val) --i;

        return max(len, (max_val - 1) * (n + 1) + 25 - i);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值