Replay an Operation Log on a Max-Heap With a Replace-Top Command
Solve this ProblemRun a log of operations against an initially empty max-heap: insert a value, extractMax (remove and report the largest value), peekMax (report it without removing) and replaceMax — which removes the current largest value, adds a new value in its place, and reports the value it removed. Return the answer reported by every extractMax, peekMax and replaceMax, in order; any of them on an empty heap reports -1 (and replaceMax still adds its value).
Re-sorting the whole list before every query gives the right answers, but does far more work than the question needs. A binary max-heap keeps only the maximum in a known place and repairs itself along one path after each change — and replaceMax shows a neat shortcut: overwrite the root and sift down once, instead of extracting and inserting.
Test Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
1 ≤ ops.length ≤ 100, and values.length equals ops.length - ◆
Each op is "insert", "extractMax", "peekMax" or "replaceMax"; the matching values entry is only read for "insert" and "replaceMax" - ◆
0 ≤ values[i] ≤ 1000 - ◆
"replaceMax v" removes the current maximum, adds v, and reports the removed maximum; on an empty heap it just adds v and reports -1 - ◆
"extractMax" and "peekMax" on an empty heap report -1
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Re-Sort the Whole List Before Every Query
BruteKeep every value in a plain list and, each time the maximum is needed (peekMax, extractMax or replaceMax), sort the entire list from largest to smallest so the maximum sits at the front. extractMax then drops the front element and replaceMax overwrites it with the new value. The answers are right, but every query pays for a full O(n log n) sort even though only a single element — the maximum — was ever needed.
O(n² log n)O(n)1class Solution {
2 public List<Integer> replayMaxHeap(String[] ops, int[] values) {
3 List<Integer> items = new ArrayList<>();
4 List<Integer> answers = new ArrayList<>();
5 for (int i = 0; i < ops.length; i++) {
6 String op = ops[i];
7 if (op.equals("insert")) {
8 items.add(values[i]);
9 } else if (items.isEmpty()) {
10 answers.add(-1);
11 if (op.equals("replaceMax")) items.add(values[i]);
12 } else {
13 items.sort(Collections.reverseOrder());
14 answers.add(items.get(0));
15 if (op.equals("extractMax")) {
16 items.remove(0);
17 } else if (op.equals("replaceMax")) {
18 items.set(0, values[i]);
19 }
20 }
21 }
22 return answers;
23 }
24}Optimal — Array-Backed Binary Max-Heap With sift-up and sift-down
OptimalKeep the values in a binary max-heap: an array where the children of index i are 2i+1 and 2i+2 and every parent is ≥ its children, so the maximum is always at index 0 and peekMax is free. insert appends and sifts up. extractMax moves the last element to the root and sifts down. replaceMax is a special case that does the same work in one pass: instead of extracting and then inserting (two repairs), it simply overwrites the root with the new value and sifts it down once — if the new value is still the largest it doesn't move at all.
O(n log n)O(n)1class Solution {
2 public List<Integer> replayMaxHeap(String[] ops, int[] values) {
3 int[] heap = new int[ops.length];
4 int size = 0;
5 List<Integer> answers = new ArrayList<>();
6 for (int i = 0; i < ops.length; i++) {
7 String op = ops[i];
8 if (op.equals("insert")) {
9 heap[size] = values[i];
10 siftUp(heap, size++);
11 } else if (size == 0) {
12 answers.add(-1);
13 if (op.equals("replaceMax")) {
14 heap[size] = values[i];
15 siftUp(heap, size++);
16 }
17 } else if (op.equals("peekMax")) {
18 answers.add(heap[0]);
19 } else if (op.equals("extractMax")) {
20 answers.add(heap[0]);
21 heap[0] = heap[--size];
22 siftDown(heap, size, 0);
23 } else {
24 answers.add(heap[0]);
25 heap[0] = values[i];
26 siftDown(heap, size, 0);
27 }
28 }
29 return answers;
30 }
31
32 private void siftUp(int[] heap, int child) {
33 while (child > 0 && heap[(child - 1) / 2] < heap[child]) {
34 int parent = (child - 1) / 2;
35 int tmp = heap[parent]; heap[parent] = heap[child]; heap[child] = tmp;
36 child = parent;
37 }
38 }
39
40 private void siftDown(int[] heap, int size, int node) {
41 while (true) {
42 int left = 2 * node + 1, right = left + 1, largest = node;
43 if (left < size && heap[left] > heap[largest]) largest = left;
44 if (right < size && heap[right] > heap[largest]) largest = right;
45 if (largest == node) break;
46 int tmp = heap[node]; heap[node] = heap[largest]; heap[largest] = tmp;
47 node = largest;
48 }
49 }
50}