Solution: A Queue of Task Indices
We still need a way to tell which task is at the front of the queue. One option is to write a class that represents a task, with two fields: the burst time and the task’s index in the input array. We would enqueue instances of that class. The two values would be stored together in the queue, so the front of the queue would tell us both the burst time and which task it belongs to.
That would work. But when a task reaches the front, what we actually need is its index, so we can update its burst time and eventually record its finish time in the output array at that same index. We already store the burst times in the burstTimes array and the finish times in the completion array, so the index lets us look up and update the task’s entry in both arrays. So the queue does not need the burst times at all. We enqueue the indices.
The burst times change as tasks run. So we need to update the values in the burstTimes array. This is not the best practice, because it modifies the input array. The caller may still need the input array. A better approach is to make a local copy of the burst times array and work with that copy instead. I am going to call this copy remaining because it keeps track of how much time is left for each task.
Let’s update the code from earlier and implement this idea:
public static int[] completionTimes(int[] burstTimes, int quantum) {
int n = burstTimes.length;
int[] remaining = new int[n]; // how much time is left for each task
int[] completion = new int[n]; // the finish time of each task
Queue<Integer> line = new LinkedQueue<>(); // the queue of task indices
// initialize the remaining times and enqueue the task indices
for (int i = 0; i < n; i++) {
remaining[i] = burstTimes[i]; // initially, all of the burst time remains
line.enqueue(i);
}
int time = 0; // the current time
while (!line.isEmpty()) {
int taskIndex = line.front();
line.dequeue();
int run = Math.min(quantum, remaining[taskIndex]); // a full slice, or whatever is left
time += run;
remaining[taskIndex] -= run;
if (remaining[taskIndex] == 0) {
completion[taskIndex] = time; // the task is done; record when
} else {
line.enqueue(taskIndex); // not done; back of the line
}
}
return completion;
}
The loop is the same as our first attempt. The difference is that the values in the queue are now indices, and we use remaining[taskIndex] where we used to use the dequeued burst time.
I used a LinkedQueue here, but that does not matter. Any queue implementation would work.
When we stated the problem, we listed some edge cases. Let’s check the solution against them.
- If
burstTimesis empty, the queue starts empty, the loop never runs, and we return an empty array. - If every burst time is at most
quantum, thenMath.min(quantum, remaining[taskIndex])runs each task to completion on its first turn, and no task is ever re-enqueued: the tasks run one after another, in input order. - A task that finishes exactly at the end of its slice has
remaining[taskIndex] == 0, so it is recorded as done instead of being re-enqueued with no work left. TheMath.minline covers both “each task gets at mostquantumunits” and this boundary case.
You can also check the method against our running example: with burstTimes = [9, 2, 7] and quantum = 4, it returns [18, 6, 17].
The pattern here is using a queue to simulate a waiting line.