HEAP (PRIORITY QUEUE) › INTRODUCTORY QUESTIONS
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →A max-priority queue must return the greatest value currently stored. A binary max-heap makes that value easy to find: the root is always greater than or equal to both children, and the same rule holds recursively for every subtree. Therefore the value to extract is simply the element at index 0 of the heap array.
Insertion may disturb the rule only along the new value's path to the root. Place the value at the next free position, then swap it with its parent while it is larger. Extraction has the reverse shape: save the root, move the last element into index 0, and repeatedly swap it with its larger child until neither child is larger. Choosing the larger child is essential because the parent must remain at least as large as both children.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n log n) | The ith insertion can move up at most the heap height, so summing the worst-case O(log n) work over n insertions gives O(n log n). The one extraction moves down at most the same height and adds O(log n), which does not change the total bound. |
| Space | O(n) extra | The heap array stores all n values before the extraction, so its working storage is O(n). The returned integer is output and is excluded from the space bound. The downward walk itself uses O(1) additional variables; even in the worst input shape, the heap array remains O(n). |
#include <algorithm>
#include <vector>
using namespace std;
class Solution {
public:
int extractMax(vector<int> values) {
vector<int> heap;
heap.reserve(values.size());
for (int x : values) {
heap.push_back(x);
int i = static_cast<int>(heap.size()) - 1;
while (i > 0) {
int parent = (i - 1) / 2;
if (heap[parent] >= heap[i]) break;
swap(heap[parent], heap[i]);
i = parent;
}
}
int answer = heap[0];
heap[0] = heap.back();
heap.pop_back();
int i = 0;
while (true) {
int best = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < static_cast<int>(heap.size()) &&
heap[left] > heap[best]) {
best = left;
}
if (right < static_cast<int>(heap.size()) &&
heap[right] > heap[best]) {
best = right;
}
if (best == i) break;
swap(heap[i], heap[best]);
i = best;
}
return answer;
}
};The two index updates are the lines that preserve the running-time bound. After an upward swap, i becomes the parent's index, so the loop examines the next level rather than restarting. After a downward swap, i becomes the selected child's index, so the replacement is followed along the only path where a violation can remain. The comparisons use >= for the upward stop and > for child selection, allowing duplicate values to remain in place without affecting correctness.
You can append all values first and heapify the array from the last internal node back to the root. This is an optimisation: it builds the heap in O(n) time instead of simulating n separate insertions in O(n log n). The extraction step is unchanged and costs O(log n). Bottom-up heapify is usually the better implementation when you only need the final priority queue, while repeated insertion mirrors the stated insertion process more directly.
#include <algorithm>
#include <vector>
using namespace std;
class Solution {
private:
void siftDown(vector<int>& heap, int i) {
while (true) {
int best = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < static_cast<int>(heap.size()) &&
heap[left] > heap[best]) {
best = left;
}
if (right < static_cast<int>(heap.size()) &&
heap[right] > heap[best]) {
best = right;
}
if (best == i) break;
swap(heap[i], heap[best]);
i = best;
}
}
public:
int extractMax(vector<int> values) {
vector<int> heap = values;
for (int i = static_cast<int>(heap.size()) / 2 - 1; i >= 0; --i) {
siftDown(heap, i);
}
int answer = heap[0];
heap[0] = heap.back();
heap.pop_back();
if (!heap.empty()) siftDown(heap, 0);
return answer;
}
};