[LeetCode] Task Scheduler 任务行程表

简介: Given a char array representing tasks CPU need to do. It contains capital letters A to Z where different letters represent different tasks.

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].

这道题让我们安排CPU的任务,规定在两个相同任务之间至少隔n个时间点。说实话,刚开始博主并没有完全理解题目的意思,后来看了大神们的解法才悟出个道理来。下面这种解法参考了大神fatalme的帖子,由于题目中规定了两个相同任务之间至少隔n个时间点,那么我们首先应该处理的出现次数最多的那个任务,先确定好这些高频任务,然后再来安排那些低频任务。如果任务F的出现频率最高,为k次,那么我们用n个空位将每两个F分隔开,然后我们按顺序加入其他低频的任务,来看一个例子:

AAAABBBEEFFGG 3

我们发现任务A出现了4次,频率最高,于是我们在每个A中间加入三个空位,如下:

A---A---A---A

AB--AB--AB--A   (加入B)

ABE-ABE-AB--A   (加入E)

ABEFABE-ABF-A   (加入F,每次尽可能填满或者是均匀填充)

ABEFABEGABFGA   (加入G)

再来看一个例子:

ACCCEEE 2

我们发现任务C和E都出现了三次,那么我们就将CE看作一个整体,在中间加入一个位置即可:

CE-CE-CE

CEACE-CE   (加入A)

注意最后面那个idle不能省略,不然就不满足相同两个任务之间要隔2个时间点了。

这道题好在没有让我们输出任务安排结果,而只是问所需的时间总长,那么我们就想个方法来快速计算出所需时间总长即可。我们仔细观察上面两个例子可以发现,都分成了(mx - 1)块,再加上最后面的字母,其中mx为最大出现次数。比如例子1中,A出现了4次,所以有A---模块出现了3次,再加上最后的A,每个模块的长度为4。例子2中,CE-出现了2次,再加上最后的CE,每个模块长度为3。我们可以发现,模块的次数为任务最大次数减1,模块的长度为n+1,最后加上的字母个数为出现次数最多的任务,可能有多个并列。这样三个部分都搞清楚了,写起来就不难了,我们统计每个大写字母出现的次数,然后排序,这样出现次数最多的字母就到了末尾,然后我们向前遍历,找出出现次数一样多的任务个数,就可以迅速求出总时间长了,参见代码如下: 

解法一:

public:
    int leastInterval(vector<char>& tasks, int n) {
        vector<int> cnt(26, 0);
        for (char task : tasks) {
            ++cnt[task - 'A'];
        }
        sort(cnt.begin(), cnt.end());
        int i = 25, mx = cnt[25], len = tasks.size();
        while (i >= 0 && cnt[i] == mx) --i;
        return max(len, (mx - 1) * (n + 1) + 25 - i);
    }
};

下面这种解法是根据大神jinzhou的帖子,优点是代码更容易读懂,而且变量命名很reasonable,前半部分都是一样的,求出最多的次数mx,还有同时出现mx次的不同任务的个数mxCnt。这个解法的思想是先算出所有空出来的位置,然后计算出所有需要填入的task的个数,如果超出了空位的个数,就需要最后再补上相应的个数。注意这里如果有多个任务出现次数相同,那么将其整体放一起,就像上面的第二个例子中的CE一样,那么此时每个part中的空位个数就是n - (mxCnt - 1),那么空位的总数就是part的总数乘以每个part中空位的个数了,那么我们此时除去已经放入part中的,还剩下的task的个数就是task的总个数减去mx * mxCnt,然后此时和之前求出的空位数相比较,如果空位数要大于剩余的task数,那么则说明还需补充多余的空位,否则就直接返回task的总数即可,参见代码如下:

解法二:

public:
    int leastInterval(vector<char>& tasks, int n) {
        int mx = 0, mxCnt = 0;
        vector<int> cnt(26, 0);        
        for (char task : tasks) {
            ++cnt[task - 'A'];
            if (mx == cnt[task - 'A']) {
                ++mxCnt;
            } else if (mx < cnt[task - 'A']) {
                mx = cnt[task - 'A'];
                mxCnt = 1;
            }
        }
        int partCnt = mx - 1;
        int partLen = n - (mxCnt - 1);
        int emptySlots = partCnt * partLen;
        int taskLeft = tasks.size() - mx * mxCnt;
        int idles = max(0, emptySlots - taskLeft);
        return tasks.size() + idles;
    }
};

下面这种解法是参考的大神alexander的解法,思路是建立一个优先队列,然后把统计好的个数都存入优先队列中,那么大的次数会在队列的前面。这题还是要分块,每块能装n+1个任务,装任务是从优先队列中取,每个任务取一个,装到一个临时数组中,然后遍历取出的任务,对于每个任务,将其哈希表映射的次数减1,如果减1后,次数仍大于0,则将此任务次数再次排入队列中,遍历完后如果队列不为空,说明该块全部被填满,则结果加上n+1。我们之前在队列中取任务是用个变量cnt来记录取出任务的个数,我们想取出n+1个,如果队列中任务数少于n+1个,那就用cnt来记录真实取出的个数,当队列为空时,就加上cnt的个数,参见代码如下:

解法三:

public:
    int leastInterval(vector<char>& tasks, int n) {
        int res = 0, cycle = n + 1;
        unordered_map<char, int> m;
        priority_queue<int> q;
        for (char c : tasks) ++m[c];
        for (auto a : m) q.push(a.second);
        while (!q.empty()) {
            int cnt = 0;
            vector<int> t;
            for (int i = 0; i < cycle; ++i) {
                if (!q.empty()) {
                    t.push_back(q.top()); q.pop();
                    ++cnt;
                }
            }
            for (int d : t) {
                if (--d > 0) q.push(d);
            }
            res += q.empty() ? cnt : cycle;
        }
        return res;
    }
};

参考资料:

https://discuss.leetcode.com/topic/92873/c-java-clean-code-priority-queue/2

https://discuss.leetcode.com/topic/92852/concise-java-solution-o-n-time-o-26-space

https://discuss.leetcode.com/topic/92966/java-o-n-time-o-1-space-1-pass-no-sorting-solution-with-detailed-explanation/2

本文转自博客园Grandyang的博客,原文链接:[LeetCode] Task Scheduler 任务行程表

,如需转载请自行联系原博主。

相关文章
|
Java 调度
Leetcode-Medium 621. Task Scheduler
Leetcode-Medium 621. Task Scheduler
118 0
Leetcode-Medium 621. Task Scheduler
|
3月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
63 6
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
57 4
|
4月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
128 2
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
49 1
|
3月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
4月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
75 7
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 22. 链表中倒数第k个节点
Leetcode题目"剑指 Offer 22. 链表中倒数第k个节点"的Python解决方案,使用双指针法找到并返回链表中倒数第k个节点。
58 5
|
4月前
|
Python
【Leetcode刷题Python】剑指 Offer 30. 包含min函数的栈
本文提供了实现一个包含min函数的栈的Python代码,确保min、push和pop操作的时间复杂度为O(1)。
35 4