Solution: Use an Adaptive Priority Queue
The adaptive priority queue has an operation for each kind of event, so each event becomes a call or two:
public static List<String> serveOrder(String[][] events) {
AdaptivePriorityQueue<String, Integer> pq = new IndexedArrayHeap<>();
List<String> served = new ArrayList<>();
for (String[] e : events) {
if (e[0].equals("add")) {
pq.add(e[1], Integer.parseInt(e[2]));
} else if (e[0].equals("change")) {
pq.changePriority(e[1], Integer.parseInt(e[2]));
} else { // "serve"
served.add(pq.peek()); // the highest-priority task
pq.remove();
}
}
return served;
}
A serve event reads the top with peek and then takes it out with remove. These are the same two steps the priority queue chapter used to serve a task.
Let’s trace the example. After each event, the diagram shows the waiting tasks in priority order, and peek marks the task that would be served next.
add A 5, add B 3, add C 8: the queue now holds the three tasks in priority order.
┌─────┬─────┬─────┐
│ B:3 │ A:5 │ C:8 │ size = 3
└─────┴─────┴─────┘
▲
peek → B:3 (min)
change C 1: C’s priority drops to 1, which makes C the highest-priority task.
┌─────┬─────┬─────┐
│ C:1 │ B:3 │ A:5 │ size = 3
└─────┴─────┴─────┘
▲
peek → C:1 (min)
serve: the highest priority is C at 1. Served so far: [C].
┌─────┬─────┐
│ B:3 │ A:5 │ size = 2
└─────┴─────┘
▲
peek → B:3 (min)
change A 2: A’s priority drops to 2.
┌─────┬─────┐
│ A:2 │ B:3 │ size = 2
└─────┴─────┘
▲
peek → A:2 (min)
serve: the highest priority is A at 2, ahead of B at 3. Served so far: [C, A].
┌─────┐
│ B:3 │ size = 1
└─────┘
▲
peek → B:3 (min)
serve: only B is left. Served: [C, A, B].
For events on a queue of up to tasks, each event does a constant number of queue operations. With the TreeMap index map, each of those operations is in the worst case, so the whole run is . A plain priority queue would have to find the task for every change, and that is an scan. So on a run with many changes, the adaptive queue is much faster. The index map is part of IndexedArrayHeap, so we can replace it with a constant-time map without changing serveOrder. With that map, every event drops to .