Data Structures & Algorithms · Data Structures & Algorithms
Heaps and Priority Queues
Binary heaps and priority queues for efficient access to minimum or maximum elements.
Eight concepts on binary heaps and priority queues — the heap property, array indexing, sift-up/sift-down, linear-time build-heap, k-th largest with a size-k min-heap, and merging k sorted lists. Peek is O(1); every structural change is O(\log n).
- Data Structures & Algorithms
- Medium level
- 8 concepts
- 5 practice questions
1Binary heap: complete tree + order property
A binary heap is a complete binary tree — every level filled left to right — stored so the extreme element sits at the root. In a min-heap every parent is \leq its children; in a max-heap every parent is \geq its children. Completeness keeps the tree height \lfloor \log_2 n \rfloor; the order property makes peek-min or peek-max O(1).
The heap is not a BST: in-order traversal is not sorted. Only the root is guaranteed extreme; the second-smallest (in a min-heap) may sit in either subtree.
Figure. Root 1 is the minimum. Every parent is ≤ its children; the tree is complete. In-order is 7, 3, 4, 1, 5, 2 — not sorted.
What the shape guarantees
- Complete treeNodes pack left to right; height is \Theta(\log n).
- Heap orderMin-heap: parent ≤ children. Max-heap: parent ≥ children.
- PeekRead index 0 (the root) in O(1) — no walk.
In a min-heap, the smallest element is
- Always at the root
- Always at the leftmost leaf
- Found by an in-order traversal, as in a BST
Heap order puts the minimum at the root. Leaves can hold large values; in-order is not sorted in a heap.
2Array layout: children at 2i+1 and 2i+2
Because the tree is complete, a heap stores nodes in an array in level order with no gaps. For the node at index i (0-based), the left child is at 2i+1, the right child at 2i+2, and the parent at \lfloor (i-1)/2 \rfloor. No explicit child pointers are required.
Navigating by index arithmetic is O(1) per hop; a sift walks O(\log n) hops along a root-to-leaf path.
Figure. Cells store value after index. Parent of index 5 is 2; children of 1 are 3 and 4 — matching the tree.
From index to family
- Childrenleft = 2i+1, right = 2i+2 (if those indices are < n).
- Parentparent = \lfloor (i-1)/2 \rfloor for i > 0.
- RootIndex 0 has no parent; it is the extreme element.
Where is the parent of index 5?
In a 0-based heap array, what is the parent index of cell 5, and what are the child indices of cell 1?
- parent(5) = \lfloor (5-1)/2 \rfloor\lfloor 2 \rfloor = 2
- children(1): left = 2·1+1, right = 2·1+23 and 4
- Each formula is O(1) arithmeticno pointer chase
Pro tip. Off-by-one between 0-based and 1-based textbooks is the usual bug — freeze one convention and stick to it.
In a 0-based heap array, the left child of index i is at
- 2i+1
- 2i
- \lfloor (i-1)/2 \rfloor
2i+1 and 2i+2 are the children; \lfloor (i-1)/2 \rfloor is the parent formula.
3Insert and sift-up
Insert writes the new value at the next free leaf position (the end of the array) to preserve completeness, then sifts up: while the node is smaller than its parent in a min-heap (or larger in a max-heap), swap with the parent. At most one swap per level, so insert is O(\log n).
Peek stays O(1) — you do not rebuild the tree; you only repair the single path from the new leaf toward the root.

Insert into a min-heap
- AppendPlace the value at index n, then n++.
- Compare upwardWhile i > 0 and a[i] < a[parent(i)], swap them and set i = parent(i).
- StopHeap order restored on that path; height bounds the swaps by O(\log n).
Sift 0 into a small min-heap
Min-heap array [1, 3, 2, 7, 4, 5] (n = 6) receives insert(0). How many swaps occur during sift-up, and what is the root afterward?
- Append 0 at index 6 → [1, 3, 2, 7, 4, 5, 0]i = 6, parent = 2 (value 2)
- 0 < 2 → swap with index 2[1, 3, 0, 7, 4, 5, 2]; i = 2
- 0 < 1 → swap with index 0[0, 3, 1, 7, 4, 5, 2]; 2 swaps; root = 0
Pro tip. Append-first preserves completeness; sifting without appending (or inserting in the middle) would punch holes and break the array indexing formulas.
Insert into a binary heap of n elements is
- O(\log n), because sift-up climbs at most one edge per level
- O(1), because the new value always stays at the leaf
- O(n), because every element must be re-sorted
Height is \Theta(\log n) and sift-up touches one path. Leaves are not final positions when the new value is extreme; full re-sort is heap sort's cousin, not insert.
4Extract-min/max and sift-down
Extract-min on a min-heap returns the root, moves the last leaf into the root hole (to keep the tree complete), then sifts down: repeatedly swap the node with its smaller child while heap order is violated. Extract-max on a max-heap is symmetric. Cost O(\log n); peek without removal stays O(1).
Deleting an arbitrary interior node is not the common API — priority queues expose extract of the extreme element. Arbitrary delete needs an index handle and the same sift repair.
On the Binary heap: complete tree + order property tree, pull the root, move the last leaf into its place, then sift that value down by swapping with the smaller (min-heap) child until order returns.
Extract-min
- Read rootAnswer = a[0].
- Fill the holeMove a[n−1] to a[0], then n−−.
- Sift downWhile a child is smaller than the node, swap with the smaller child; stop at a leaf or when order holds.
Extract-min from [1, 3, 2, 7, 4, 5]
After extract-min on the min-heap [1, 3, 2, 7, 4, 5], what value is returned and what is the new root?
- Return root1
- Move last leaf 5 to root → [5, 3, 2, 7, 4], n = 5order broken at root
- Children of 0 are 3 and 2; smaller child is 2 at index 2; 5 > 2 → swap[2, 3, 5, 7, 4]
- Node 5 at index 2 has no children in range → stopnew root 2
Pro tip. Always sift with the extreme child (smaller in a min-heap). Swapping with either child at random can leave a parent larger than the other child.
After moving the last leaf to the root in extract-min, you restore order with
- Sift-down
- Sift-up only
- A full O(n \log n) rebuild every time
The hole was at the root; the replacement may be too large and must move down. Sift-up is for inserts at a leaf. Full rebuild works but is overkill for one extract.
5Build-heap is O(n), not O(n log n)
Heapifying an existing array bottom-up calls sift-down from the last internal node up to the root. Naively, n inserts would be O(n \log n), but most nodes sit near the leaves and sift only a short distance. The tight bound sums to O(n).
Interview takeaway: converting an unordered array into a heap is linear. Heap sort still pays O(n \log n) overall because it extracts n times after the build.
Figure. Floyd build-heap siftdown from the first non-leaf is linear; n separate inserts pay an extra \log n factor.
Bottom-up heapify
- Start at last parenti = \lfloor n/2 \rfloor - 1 down to 0.
- Sift eachsift-down(i) repairs the subtree rooted at i.
- BoundAggregate sift cost across heights is O(n), not O(n \log n).
| Method | Time to heapify n keys |
|---|---|
| n × insert | O(n \log n) |
| Bottom-up build-heap | O(n) |
| Heap sort (build + n extracts) | O(n \log n) |
Building a heap from n unordered elements bottom-up costs
- O(n)
- O(n \log n) necessarily, the same as n inserts
- O(1), because the array is already contiguous
Bottom-up sift-down is linear. n inserts are the slower alternative; contiguity alone does not create heap order.
6K-th largest with a size-k min-heap
To find the k-th largest element, maintain a min-heap of size at most k. Push each array value; whenever the heap exceeds size k, pop the smallest. After one pass, the heap holds the k largest values seen, and its root — the smallest among them — is the k-th largest overall.
Time O(n \log k) and space O(k). When k \ll n this beats sorting the whole array in O(n \log n).
Figure. Track the k largest seen so far in a size-k min-heap: the root is the current k-th largest gate.
Size-k filter
- PushInsert the next element into the min-heap.
- TrimIf size > k, extract-min (drop a value that cannot be among the k largest).
- FinishRoot of the size-k heap is the k-th largest.
K-th largest via min-heap
import heapq
def kth_largest(nums, k):
h = []
for x in nums:
heapq.heappush(h, x)
if len(h) > k:
heapq.heappop(h)
return h[0]K-th Largest Element
Given nums = [3, 2, 1, 5, 6, 4] and k = 2, find the 2nd largest element.
- Push 3, 2 → heap [2, 3] (size 2)root 2
- Push 1 → [1, 2, 3], pop 1 → [2, 3]still size 2
- Push 5 → pop 2; push 6 → pop 3; push 4 → pop 4heap [5, 6]
- Root of size-2 min-heap5 (2nd largest)
Pro tip. A size-k min-heap solves k-th largest in O(n \log k) — better than sorting when k is much smaller than n.
Coding lab. Find the 2nd largest in six numbers runs in the app, with checks on your output.
For k-th largest with a size-k min-heap, the answer is read from
- The root (minimum) of the heap of k largest values
- The maximum leaf of that heap
- The median of the original array
The heap's minimum among the k largest is exactly the k-th largest. A leaf maximum is not maintained as an API; the median is a different problem.
7Merge k sorted lists with a heap
To merge k sorted linked lists, push each list's head into a min-heap keyed by node value (at most k entries). Repeatedly pop the smallest node, append it to the result, and push that node's successor if it exists. Every node is pushed and popped once.
Total time O(N \log k) for N nodes overall — each heap operation is O(\log k), not O(\log N), because the heap never holds more than one candidate per list.
Figure. One heap entry per list head; each pop emits the next global min and pushes that list's successor — O(n\log k).
k-way merge
- SeedPush the head of each non-empty list into a min-heap.
- Pop / appendExtract-min; write that value next in the merged output.
- RefillIf that node has a next, push next; repeat until the heap is empty.
Merge k lists (heap of heads)
import heapq
def merge_k_lists(lists):
h = []
for i, node in enumerate(lists):
if node:
heapq.heappush(h, (node.val, i, node))
dummy = cur = Node(0)
while h:
val, i, node = heapq.heappop(h)
cur.next = node
cur = cur.next
if node.next:
heapq.heappush(h, (node.next.val, i, node.next))
return dummy.nextMerge three sorted lists
Merge lists [1, 4, 7], [2, 5], [3] into one sorted list using a min-heap of heads.
- Seed heap with heads 1, 2, 3heap mins → 1
- Pop 1, push successor 4; pop 2, push 5; pop 3 (list done)output prefix 1, 2, 3
- Pop 4, push 7; pop 5; pop 7merged [1, 2, 3, 4, 5, 7]; O(N \log 3)
Pro tip. The heap always holds at most k candidates, so each pop/push is O(\log k) rather than O(\log N).
Merging N nodes from k sorted lists with a heap of heads costs
- O(N \log k)
- O(N \log N) always, because the heap holds every node
- O(N + k) with comparisons only against the global minimum array
At most k heads sit in the heap, so each of N pops/pushes is O(\log k). Holding every node would be wrong; a flat scan without a heap cannot pick the next minimum among k lists in O(1).
8Where priority queues show up
Heaps power Dijkstra's algorithm (extract nearest unsettled vertex), heap sort, k-largest/k-smallest filters, and the two-heap streaming median (max-heap for the lower half, min-heap for the upper). Anytime you need repeated "give me the current extreme" under inserts, reach for a priority queue.
If you only need a one-shot minimum of a static array, a linear scan is O(n) and simpler. The heap pays off when extremes are requested many times as the set changes.
Figure. Priority queues show up wherever you repeatedly need the current best edge, task, or candidate — not as a sorted array substitute.
| Problem | Heap role |
|---|---|
| Dijkstra | Extract next closest vertex |
| Heap sort | Build-heap + n × extract-max |
| K largest | Size-k min-heap filter |
| Streaming median | Paired max-heap + min-heap |
Streaming median with two heaps keeps
- A max-heap for the lower half and a min-heap for the upper half
- A single min-heap of the entire stream only
- No heap — median requires sorting the whole stream every time
Lower half's maximum and upper half's minimum meet at the median. One min-heap alone does not expose the lower median boundary cheaply; full re-sort each time is the slow baseline heaps avoid.
Notes
- Binary Heap: A complete binary tree stored in an array where a min-heap keeps each parent <= its children (max-heap: parent >= children), giving O(1) access to the extreme element.
- Heap Operations: Insert and extract-min/max both take O(\log n) via sift-up/sift-down to restore the heap property; peek is O(1).
- Array Indexing: For a node at index i, children are at 2i+1 and 2i+2 and the parent is at (i-1)/2, so no explicit pointers are needed.
- Build-Heap: Heapifying an existing array bottom-up is O(n), not O(n \log n).
- Priority Queue Uses: Heaps power Dijkstra's algorithm, heap sort, k-largest/smallest, and streaming median (two heaps).
Formulas
- Insert and extract: O(\log n); peek min/max: O(1).
- Build-heap from n elements: O(n).
- Heap sort: O(n \log n) time, O(1) space.
- K-th largest via a size-k min-heap: O(n \log k) time, O(k) space.
- Array layout: children of i at 2i+1, 2i+2; parent at \lfloor (i-1)/2 \rfloor.
Exam traps & shortcuts
- For 'k largest/smallest elements,' keep a size-k heap (O(n \log k)) instead of fully sorting (O(n \log n)).
- Use a min-heap to always pop the current smallest - the basis of Dijkstra and merge-k-sorted-lists.
- Maintain a running median with two heaps: a max-heap for the lower half and a min-heap for the upper half.
- Remember building a heap from an array is O(n); only repeated extraction adds the log factors.
Reference tables
Restated from the concepts above.
| Operation | Time | Notes |
|---|---|---|
| Peek min/max | O(1) | Read root |
| Insert / extract | O(\log n) | Sift-up / sift-down |
| Build-heap | O(n) | Bottom-up sift-down |
| Heap sort | O(n \log n) | O(1) extra space typical |
| K-th largest (size-k heap) | O(n \log k) | O(k) space |
| Merge k sorted lists | O(N \log k) | Heap holds ≤ k heads |
Index arithmetic and the bounds the ledgers use.
| Identity | Form |
|---|---|
| Children of i | 2i+1, 2i+2 |
| Parent of i | \lfloor (i-1)/2 \rfloor |
| Height | \lfloor \log_2 n \rfloor |
| K-th largest root | min of the k largest = k-th largest |
Recap
Night-before heap pegs.
- Shape
- Complete binary tree + parent≤children (min) or ≥ (max). Not a BST.
- Array
- Children at 2i+1 / 2i+2; parent at ⌊(i−1)/2⌋.
- Update
- Insert sifts up; extract replaces root and sifts down — both O(\log n).
- Build
- Bottom-up heapify is O(n), not O(n \log n).
- K-th
- Size-k min-heap; root is k-th largest in O(n \log k).
- Merge k
- Heap of heads → O(N \log k).
- Uses
- Dijkstra, heap sort, streaming median — repeated extremes under change.
Practise Heaps and Priority Queues
Reading is free and needs no account. Practice, mocks and progress live in the app.
- 5 exam-style questions on this topic, with explanations
- A 6-question practice set that ends the chapter
- Timed mocks scored with the real marking scheme
- Readiness tracked per topic, kept on your device