Leetcode-优先队列-621. 任务调度器

题目:

621. 任务调度器

难度中等336

给定一个用字符数组表示的 CPU 需要执行的任务列表。其中包含使用大写的 A - Z 字母表示的26 种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。CPU 在任何一个单位时间内都可以执行一个任务,或者在待命状态。

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

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

示例 :

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

提示:

  1. 任务的总个数为 [1, 10000]
  2. n 的取值范围为 [0, 100]

题解:

这道题说实话半天没转过弯来怎么用队列做

想了半天也是用数组统计任务种类,sort排序,还以为用队列能更快,想了半天也就感觉priorityqueue还有点边

不过还是无思路,好家伙,一看官方题解,队列只是充当排序的工具,怎么?sort它不香吗?Array它不快吗?

然后说实话,解法1,2我都没看懂,看了解法三,才迷迷糊糊。

直到看到这个方法(from ant-chen):

核心思想就是找到出现次数最多的字符(记为字符x),把那个按间隔排开。其他字符往间隔插入。分为几种情况。
1 有次数和x一样的字符,这些字符会加长最终长度。因为这些字符的最后一个会排在x的后面
2 空格不够的,会加大最终长度。因为要在间隔插入空格。
3 其余情况不会增加最终长度

如: ['A','A','A','A','B','B','B','B','C','C','D','D','E'] 2
先把A排开, 变成 A - - A - - A - - A
插入B,变成 A B - A B - A B - A B (由于B和A一样是三次,故最后长度+1)
插入C,变成 A B C A B C A B - A B (此时还有空格,长度不变)
插入第一个D, 变成 A B C A B C A B D A B (此时还有空格,长度不变)
插入第二个D, 此时无空格,D随意插入(只要不在上一个D旁边),长度必然+1,如:A B C D A B C A B D A B E
插入E, 此时无空格,E随意插入位置,长度必然+1,如:A B C D E A B C A B D A B E

具体计算见代码

代码:

class Solution {
    public int leastInterval(char[] tasks, int n) {
        //统计出现次数,排序
		int[] counts = new int[26];
		for(char ch: tasks){
			counts[ch-'A']++;
		}
		Arrays.sort(counts);
		
		int maxCycle = counts[25];                    //找到出现次数最多的是多少次
		int emptyPosition = (maxCycle-1) * (n + 1);   //最少需要的初始位置数(没把最后一个 maxCycle计入)
		int result = emptyPosition;                   //最后结果必不少于空位数
		int element = 0;                              //所有任务需要占据的总位置数
		for(int count : counts){
			if(count == maxCycle){
				result++;              //最长的任务有几个,后面续几个位置
				element += count-1;    //该任务需要占据的初始位置数(减去了后面续的一个位置)
			}else{
				element += count;      //不是最长的任务后面不给续,所以都计入
			}
		}
		if(element <= emptyPosition){ //如果要占的数字小于初始数,就直接返回结果
		}else{
			return result + (element - emptyPosition);//否则返回结果+多出的个数
		}
    }
}

其实这个代码又可以用一个公式直接代替,使用公式的代码(python解法)如下:Hash其实是个字典

class Solution:
    def leastInterval(self, tasks: List[str], n: int) -> int:
        Hash = {}
        for i in tasks:
            Hash[i] = Hash.get(i, 0) + 1

        task_max = max(Hash.values())
        residual = 0
        for key in Hash.keys():
            if Hash[key] == task_max:
                residual += 1
        res = (task_max-1) * (n+1) + residual

        return res if res >= len(tasks) else len(tasks)

如果想看懂官方解法1,2,可以看一下这个代码(go-go-49)

class Solution {
    public int leastInterval(char[] tasks, int n) {
        int []num=new int[26];
        for(char c:tasks)
        {
            num[c-'A']++;//统计个数

        }
        int time=0;
        Arrays.sort(num);
        while (num[25]>0)
        {
            int i=0;
            while (i<=n)//分配个数最多的n个任务
            {
                if(num[25]==0)//全部分配完
                    break;
                if(i<26&&num[25-i]>0)//分配任务
                    num[25-i]--;
                time++;
                i++;

            }
            Arrays.sort(num);

        }
return  time;
    }
}

为了再次学习队列知识,把用优先队列的解法2放一下

在选择每一轮中的任务时,我们也可以用优先队列(堆)来代替排序。在一开始,我们把所有的任务加入到优先队列中。在每一轮,我们从优先队列中选择最多 n + 1 个任务,把它们的数量减去 1,再放回堆中(如果数量不为 0),直到堆为空。

public class Solution {
    public int leastInterval(char[] tasks, int n) {
        int[] map = new int[26];
        for (char c: tasks)
            map[c - 'A']++;
        PriorityQueue < Integer > queue = new PriorityQueue < > (26, Collections.reverseOrder());
        for (int f: map) {
            if (f > 0)
                queue.add(f);
        }
        int time = 0;
        while (!queue.isEmpty()) {
            int i = 0;
            List < Integer > temp = new ArrayList < > ();
            while (i <= n) {
                if (!queue.isEmpty()) {
                    if (queue.peek() > 1)
                        temp.add(queue.poll() - 1);
                    else
                        queue.poll();
                }
                time++;
                if (queue.isEmpty() && temp.size() == 0)
                    break;
                i++;
            }
            for (int l: temp)
                queue.add(l);
        }
        return time;
    }
}

其他:

在想用什么队列能实现思路时检索的一些队列相关资料

1,java队列——queue详细分析

2,Java的优先队列PriorityQueue详解​​​​​​​

3,Java双向队列Deque栈与队列

4,Java ArrayDequeDescendingIterator()使用及代码示例

5,Java Vector遍历的五种方法

 

 

 

 

你可能感兴趣的:(#,LeetCode)