Skip to content

621. Task Scheduler

You are given an array of CPU tasks, each represented by letters A to Z, and a cooling time, n. Each cycle or interval allows the completion of one task. Tasks can be completed in any order, but there's a constraint: identical tasks must be separated by at least n intervals due to cooling time.

Return the minimum number of intervals required to complete all tasks.

Example 1:

Input: tasks = ["A","A","A","B","B","B"], n = 2

Output: 8

Explanation: A possible sequence is: A -> B -> idle -> A -> B -> idle -> A -> B.

After completing task A, you must wait two cycles before doing A again. The same applies to task B. In the 3rd interval, neither A nor B can be done, so you idle. By the 4th cycle, you can do A again as 2 intervals have passed.

Example 2:

Input: tasks = ["A","C","A","B","D","B"], n = 1

Output: 6

Explanation: A possible sequence is: A -> B -> C -> D -> A -> B.

With a cooling interval of 1, you can repeat a task after just one other task.

Example 3:

Input: tasks = ["A","A","A", "B","B","B"], n = 3

Output: 10

Explanation: A possible sequence is: A -> B -> idle -> idle -> A -> B -> idle -> idle -> A -> B.

There are only two types of tasks, A and B, which need to be separated by 3 intervals. This leads to idling twice between repetitions of these tasks.

Constraints:

  • 1 <= tasks.length <= 104
  • tasks[i] is an uppercase English letter.
  • 0 <= n <= 100

Solution:

class Solution {
    public int leastInterval(char[] tasks, int n) {
        int[] freqMap = new int[26];
        for (char c : tasks){
            freqMap[c - 'A']++;
        }

        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(Collections.reverseOrder());

        for (int t : freqMap){
            if (t > 0){
                maxHeap.offer(t);
            }
        }

        Queue<int[]> coolingPeriodQueue = new LinkedList<>();

        int time = 0;

        while(!maxHeap.isEmpty() || !coolingPeriodQueue.isEmpty()){
            if (!maxHeap.isEmpty()){
                int t = maxHeap.poll() - 1;
                if (t > 0){
                    coolingPeriodQueue.offer(new int[]{t, time + n});
                }
            }

            if (!coolingPeriodQueue.isEmpty() && coolingPeriodQueue.peek()[1] == time){
                maxHeap.offer(coolingPeriodQueue.poll()[0]);
            }

            time++;
        }

        return time;
    }
}
class Solution {
    public int leastInterval(char[] tasks, int n) {

        // We need to schedule the tasks starting from the task with higher freq first.
        // So we use a max heap to always schedule the max available freq.

        int[] freqMap = new int[26];
        for (char c: tasks) { // We will make the array tasks into a freq map.
            freqMap[c - 'A']++;
        }

        PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b.compareTo(a));
        for (int t: freqMap) {
            if (t > 0) {
                maxHeap.offer(t);
            }
        }

        // We create a queue to help us to keep the tasks that are in cooling period.
        Queue<int[]> coolingPeriodQueue = new LinkedList<>();
        int time = 0;
        while (!maxHeap.isEmpty() || !coolingPeriodQueue.isEmpty()) {
            if (!maxHeap.isEmpty()) {
                int t = maxHeap.poll() - 1;
                if (t > 0) {
                    coolingPeriodQueue.offer(new int[] {t, time + n});
                }
            }

            if (!coolingPeriodQueue.isEmpty() && coolingPeriodQueue.peek()[1] == time) {
                maxHeap.offer(coolingPeriodQueue.poll()[0]);
            }
            time++; // Every time we schedule a task, one unit of time passes.
        }
        return time;
    }
}


// TC: O(nlogn)
// SC: O(n)