E ExamMaster

CS Core & Software Engineering · 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).

  • CS Core & Software Engineering
  • 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

  1. Complete treeNodes pack left to right; height is \Theta(\log n).
  2. Heap orderMin-heap: parent ≤ children. Max-heap: parent ≥ children.
  3. PeekRead index 0 (the root) in O(1) — no walk.
In a min-heap, the smallest element is
  1. Always at the root
  2. Always at the leftmost leaf
  3. 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

  1. Childrenleft = 2i+1, right = 2i+2 (if those indices are < n).
  2. Parentparent = \lfloor (i-1)/2 \rfloor for i > 0.
  3. 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
  1. 2i+1
  2. 2i
  3. \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.

Animation of min-heap insert: a new leaf appears at the next free position, then swaps up the path until the heap order property holds.
Insert at the next leaf, then sift up — at most one swap per level, O(\log n).

Insert into a min-heap

  1. AppendPlace the value at index n, then n++.
  2. Compare upwardWhile i > 0 and a[i] < a[parent(i)], swap them and set i = parent(i).
  3. 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
  1. O(\log n), because sift-up climbs at most one edge per level
  2. O(1), because the new value always stays at the leaf
  3. 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

  1. Read rootAnswer = a[0].
  2. Fill the holeMove a[n−1] to a[0], then n−−.
  3. 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
  1. Sift-down
  2. Sift-up only
  3. 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

  1. Start at last parenti = \lfloor n/2 \rfloor - 1 down to 0.
  2. Sift eachsift-down(i) repairs the subtree rooted at i.
  3. BoundAggregate sift cost across heights is O(n), not O(n \log n).
Build vs repeated insert
MethodTime to heapify n keys
n × insertO(n \log n)
Bottom-up build-heapO(n)
Heap sort (build + n extracts)O(n \log n)
Building a heap from n unordered elements bottom-up costs
  1. O(n)
  2. O(n \log n) necessarily, the same as n inserts
  3. 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

  1. PushInsert the next element into the min-heap.
  2. TrimIf size > k, extract-min (drop a value that cannot be among the k largest).
  3. 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
  1. The root (minimum) of the heap of k largest values
  2. The maximum leaf of that heap
  3. 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

  1. SeedPush the head of each non-empty list into a min-heap.
  2. Pop / appendExtract-min; write that value next in the merged output.
  3. 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.next

Merge 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
  1. O(N \log k)
  2. O(N \log N) always, because the heap holds every node
  3. 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.

Classic uses
ProblemHeap role
DijkstraExtract next closest vertex
Heap sortBuild-heap + n × extract-max
K largestSize-k min-heap filter
Streaming medianPaired max-heap + min-heap
Streaming median with two heaps keeps
  1. A max-heap for the lower half and a min-heap for the upper half
  2. A single min-heap of the entire stream only
  3. 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.

Complexity cheatsheet
OperationTimeNotes
Peek min/maxO(1)Read root
Insert / extractO(\log n)Sift-up / sift-down
Build-heapO(n)Bottom-up sift-down
Heap sortO(n \log n)O(1) extra space typical
K-th largest (size-k heap)O(n \log k)O(k) space
Merge k sorted listsO(N \log k)Heap holds ≤ k heads

Index arithmetic and the bounds the ledgers use.

Formula anchors
IdentityForm
Children of i2i+1, 2i+2
Parent of i\lfloor (i-1)/2 \rfloor
Height\lfloor \log_2 n \rfloor
K-th largest rootmin 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
Continue with Google — freeNo card, no trial. Works offline once installed.