• 【leetcode热题Hot100】——任务调度器


    • 💂 个人主页:努力学习的少年
    • 🤟 版权: 本文由【努力学习的少年】原创、在CSDN首发、需要转载请联系博主
    • 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦

    一.题目

    1.题目描述

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

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

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

    示例 1:

    输入:tasks = ["A","A","A","B","B","B"], n = 2
    输出:8
    解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B
         在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。

    示例 2:

    输入:tasks = ["A","A","A","B","B","B"], n = 0
    输出:6
    解释:在这种情况下,任何大小为 6 的排列都可以满足要求,因为 n = 0
    ["A","A","A","B","B","B"]
    ["A","B","A","B","A","B"]
    ["B","B","B","A","A","A"]

    ...
    诸如此类

    示例3:

    输入:tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2
    输出:16
    解释:一种可能的解决方案是:

    A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> (待命) -> (待命) -> A -> (待命) -> (待命) -> A

    提示:

    • 1 <= task.length <= 104
    • tasks[i] 是大写英文字母
    • n 的取值范围为 [0, 100]

    2.原始框架

    1. class Solution {
    2. public:
    3. int leastInterval(vector<char>& tasks, int n) {
    4. }
    5. };

    3.原题链接

    621. 任务调度器

    二.解题报告

    1.思路

    • 因为 n 是相同两个任务执行的冷却时间,也就是说,在time秒中执行了A任务,则time+n秒内就不能再执行A任务
    • 如果相同的任务堆积在一起运行,则待命的时间就越长,因为它们之间运行有冷却时间,因此,为了保证运行时间较短,我们需要将最多的任务先给消耗掉,也就是先让它运行,防止堆积在末尾。

    例如:

    tasks = ["A","A","A","A","A","A","B","B","B","E","F","G"]任务,n=2,其中任务A它的数量是最多的,其次是B任务,那么我们就需要先消耗掉A任务,其次是B任务,任务A的它执行的时间线:A->.....->A->......->A->......->A->.......->A..., 我们可以在A任务的冷却时间中穿插着不同的任务,因此有:A->B->E->A->B->F->A->B->G->A->(待命)->(待命)->A->(待命)->(待命)->A.

    如果不先消耗掉较多的相同的任务,而是先消耗掉较少的任务数量,那么最后相同的任务就会堆积到一起,导致运行效率较低。例如:

    E->F->G->B->(待命)->(待命)->B->(待命)->(待命)->B->(待命)->(待命)->B->A->(待命)->(待命)->A->(待命)->(待命)->A->(待命)->(待命)->A->(待命)->(待命)....

    从上面的例子可以看到,如果相同的任务堆积在一起运行,则待命的时间就越多,因此,我们就需要先将较多的任务给消耗掉

    • 由于是任务类型是大写字母,所以我们可以创建一个26位大小的来存储相同任务的数量,其中数组的下标表示任务类型,存储的内容是任务的数量。
    •  创建一个大根堆来表示就绪队列,它是以任务的相同的任务为基准,数量最多的任务存放在堆的根节点。
    • 再一个小根堆来表示冷却队列,它是以任务执行的时间为基准,时间最早的任务存放在堆的根节点。
    • cpu开始执行时,定义一个时间time并初始为0,记录时间,之后每次执行或者待命,time就++。
    • 就先从冷却队列中的根节点判断是否有 执行的时间+冷却时间<当前时间的,如果有,则将冷却队列中的 根节点的任务 该任务的数量 存放到就绪队列中。
    • 然后cpu再从就绪队列中拿出根节点的任务,该任务的数量--,并将该任务从就绪队列中拿走,进入冷却时间,将它 执行的time 该任务 存放进冷却队列中。
    • 直到冷却队列和就绪队列中没有任务,则执行完毕,最终的time就为最短的时间。

     

     2.代码详解

    1. class Solution {
    2. public:
    3. int leastInterval(vector<char>& tasks, int n) {
    4. using pii=pair<int,int>;
    5. priority_queue pq1;//就绪队列
    6. priority_queue,greater> pq2;//冷却队列
    7. int arr[26]={0};
    8. //数组0下标代表的是任务A,1下标是任务B
    9. //以此类推,25下标则代表的是任务Z
    10. for(auto ch:tasks){
    11. //统计所有任务的次数
    12. arr[ch-'A']++;
    13. }
    14. for(int i=0;i<26;i++){
    15. //将任务次数不为0放进就绪队列中
    16. if(arr[i]!=0)
    17. pq1.push(pii(arr[i],i));
    18. }
    19. int time=0;
    20. while(!pq1.empty()||!pq2.empty()){
    21. if(!pq2.empty()&&pq2.top().first+n
    22. //将冷却好的任务和它的任务次数放进就绪队列中
    23. int ch=pq2.top().second;
    24. pq1.push(pii(arr[ch],ch));
    25. pq2.pop();
    26. }
    27. if(!pq1.empty()){
    28. int ch=pq1.top().second;
    29. arr[ch]--;
    30. if(arr[ch]!=0)//任务次数不为0的任务放进冷却队列中
    31. pq2.push(pii(time,ch));
    32. pq1.pop();
    33. }
    34. time++;//时间++
    35. }
    36. return time;
    37. }
    38. };

    3.总结

    priority_queue<类型>默认是大根堆,如果想要使用小根堆,需要在模板类型中多加一个greater<类型>的反函数,由于模板初始值,是从左往右初始的,因此前面的容器vector<类型>就不能省略,,因此,小根堆的类型为 priority_queue<类型,容器,greater<类型>>
     

  • 相关阅读:
    Java工具类:HttpUtil项目实战
    docker 映射端口穿透内置防火墙
    搭建一个Chatbot需要哪些条件呢?
    【Ubuntu】Ubuntu 16.04 安装新的Linux内核
    vue3自定义指令
    基于Open3D的点云处理18-重建系统
    得失权衡、主体信任与危机感知:“健康码”常态化使用的影响因素研究
    11.The Metric Tensor
    合并两个非降数组
    深入理解Redis:工程师的使用指南
  • 原文地址:https://blog.csdn.net/sjp11/article/details/126109274