超简单的leetcode解析6,5736. 单线程 CPU算法

原题

给你一个二维数组 tasks ,用于表示 n​​​​​​ 项从 0 到 n - 1 编号的任务。其中 tasks[i] = [enqueueTimei, processingTimei] 意味着第 i​​​​​​​​​​ 项任务将会于 enqueueTimei 时进入任务队列,需要 processingTimei 的时长完成执行。

现有一个单线程 CPU ,同一时间只能执行 最多一项 任务,该 CPU 将会按照下述方式运行:

如果 CPU 空闲,且任务队列中没有需要执行的任务,则 CPU 保持空闲状态。
如果 CPU 空闲,但任务队列中有需要执行的任务,则 CPU 将会选择 执行时间最短 的任务开始执行。如果多个任务具有同样的最短执行时间,则选择下标最小的任务开始执行。
一旦某项任务开始执行,CPU 在 执行完整个任务 前都不会停止。
CPU 可以在完成一项任务后,立即开始执行一项新任务。
返回 CPU 处理任务的顺序。

You are given n​​​​​​ tasks labeled from 0 to n - 1 represented by a 2D integer array tasks, where tasks[i] = [enqueueTimei, processingTimei] means that the i​​​​​​th​​​​ task will be available to process at enqueueTimei and will take processingTimei to finish processing.

You have a single-threaded CPU that can process at most one task at a time and will act in the following way:

If the CPU is idle and there are no available tasks to process, the CPU remains idle.
If the CPU is idle and there are available tasks, the CPU will choose the one with the shortest processing time. If multiple tasks have the same shortest processing time, it will choose the task with the smallest index.
Once a task is started, the CPU will process the entire task without stopping.
The CPU can finish a task then start a new one instantly.
Return the order in which the CPU will process the tasks.

示例 1:

输入:tasks = [[1,2],[2,4],[3,2],[4,1]]
输出:[0,2,3,1]
解释:事件按下述流程运行: 
- time = 1 ,任务 0 进入任务队列,可执行任务项 = {0}
- 同样在 time = 1 ,空闲状态的 CPU 开始执行任务 0 ,可执行任务项 = {}
- time = 2 ,任务 1 进入任务队列,可执行任务项 = {1}
- time = 3 ,任务 2 进入任务队列,可执行任务项 = {1, 2}
- 同样在 time = 3 ,CPU 完成任务 0 并开始执行队列中用时最短的任务 2 ,可执行任务项 = {1}
- time = 4 ,任务 3 进入任务队列,可执行任务项 = {1, 3}
- time = 5 ,CPU 完成任务 2 并开始执行队列中用时最短的任务 3 ,可执行任务项 = {1}
- time = 6 ,CPU 完成任务 3 并开始执行任务 1 ,可执行任务项 = {}
- time = 10 ,CPU 完成任务 1 并进入空闲状态
示例 2:

输入:tasks = [[7,10],[7,12],[7,5],[7,4],[7,2]]
输出:[4,3,2,0,1]
解释:事件按下述流程运行: 
- time = 7 ,所有任务同时进入任务队列,可执行任务项  = {0,1,2,3,4}
- 同样在 time = 7 ,空闲状态的 CPU 开始执行任务 4 ,可执行任务项 = {0,1,2,3}
- time = 9 ,CPU 完成任务 4 并开始执行任务 3 ,可执行任务项 = {0,1,2}
- time = 13 ,CPU 完成任务 3 并开始执行任务 2 ,可执行任务项 = {0,1}
- time = 18 ,CPU 完成任务 2 并开始执行任务 0 ,可执行任务项 = {1}
- time = 28 ,CPU 完成任务 0 并开始执行任务 1 ,可执行任务项 = {}
- time = 40 ,CPU 完成任务 1 并进入空闲状态

提示:

tasks.length == n
1 <= n <= 105
1 <= enqueueTimei, processingTimei <= 109

答案

class Solution {
    public class Task{
        public int i;
        public int intime;
        public int processTime;
        public Task(int i,int intime,int processTime){
            this.i=i;
            this.intime=intime;
            this.processTime=processTime;
        }
    }
    public int[] getOrder(int[][] tasks) {
    //入口
        List<Task> taskList=new ArrayList();
        for(int i=0;i<tasks.length;i++){
            taskList.add(new Task(i,tasks[i][0],tasks[i][1]));
        }
        Collections.sort(taskList,(a,b)->a.intime-b.intime);
        PriorityQueue<Task> queue=new PriorityQueue(new PriorityQueue<Task>((t1,t2) -> {
            if(t1.processTime == t2.processTime){
                //当执行时间相同时,根据id升序
                return t1.i - t2.i;
            }else{
                //当执行时间不同时,根据执行时间升序
                return t1.processTime - t2.processTime;
            }
        }));
        int now=0;
        int i=0;
        int length=tasks.length;
        int[] res=new int[length];
        int p=0;
        while (i<length){
            while(i<length && taskList.get(i).intime<=now){
                queue.offer(taskList.get(i));
                i++;
            }
            if(queue.isEmpty()){
                now=taskList.get(i).intime;
                while(i<length && taskList.get(i).intime==now){
                    queue.offer(taskList.get(i));
                    i++;
                }
            }
            Task task=queue.poll();
            res[p++]=task.i;
            now+=task.processTime;
        }
        while(!queue.isEmpty()){
            res[p++]=queue.poll().i;
        }
        return res;
    }
}

解析

  1. 首先为了方便计算,先定义个Task类,用来存储时间,消耗时间以及原有的index值(输出结果需要用到)
  2. 将数据放入taskList中,并按照执行时间进行排序
  3. 其次定义一个执行队列,这里没有使用普通的队列,而是使用了PriorityQueue优先队列,该列队不同于普通队列,普通队列严格遵守先进先出的规则,而优先队列可以定义优先级来决定哪个元素先出队列。
  4. 定义优先队列,优先级根据执行时间和索引值来判断优先级
  5. 定义一个now的来代表当前时间,遍历整个数据列表(taskList)
  6. 将在now时间内的数据入队,因为这里前面已经按时间排过序了,所以可以比较快的得到那些数据需要入队
  7. 当队列为空时,即当前时间段没有需要执行的任务,那么就将now跳转到最近的一个任务时间点,然后将该时间点的全部数据入队
  8. 队列此时的队首即当前时间点应该执行的任务,将任务出队同时保存数据,now加上需要执行的时间
  9. 从新开始新一轮计算,根据时间再次将数据入队
  10. 如果数据已经全部入队了,那么就按照现存的队列继续执行到最后一个任务即可得到全部结果

思路

这道题目主要就是优先队列的使用,学会优先队列的用法该题目解析起来就很简单了
还有定义一个合理的数据结构来保存数据对于代码编写是很有帮助的。
难点在于想通每个时间段会发生的时间,以时间为序,来思考逻辑。
以一个一个时间片段为单位
每次增加时间片段的时候,cpu先是要看看有没有正在执行的任务,
有正在执行的任务是不能中止的,所以当启动一个任务的时候,就可以直接跳转到任务执行完成的时间。
当cpu没有需要执行的任务的时候,就要判断哪些任务是当前时间点需要执行的任务,即在当前时间点之前的全部没有完成的任务,所以这里用到了队列来保存需要执行的任务
得到需要执行的任务了,那么哪个任务的优先级最高呢,所以这里用到了优先队列,因为不是时间越早就优先级越高

以上,个人理解,欢迎指导

你可能感兴趣的:(leetcode,面试,算法,队列,java,leetcode,算法,面试)