Knowledge / Computer Science and Engineering / Heaps and priority queues

Heaps and priority queues

Computer Science and Engineering (B.E./B.Tech) · Data structures · free study notes for exams, placements and interviews

In short

A heap is a complete binary tree stored in an array that keeps one simple rule - every parent is better than (or equal to) its children - so the best element is always at the root and reachable in O(1). A priority queue is the abstract data type (insert, find-min/max, delete-min/max, decrease-key) and a binary heap is its standard implementation, giving O(log n) updates with no pointers and no wasted memory. The pair is examined every year in GATE CS (heapify cost, build-heap derivation, heap-sort comparisons, array-index arithmetic) and in university Data Structures papers. In placement rounds it is the expected answer for "k largest elements", "merge k sorted lists", "running median" and "task scheduler", asked at Amazon, Microsoft, Google, Flipkart, Walmart, Oracle, Zoho, Adobe, Goldman Sachs, Samsung R&D and almost every product company hiring from Indian campuses.

Explained

Definitions and the two heap properties

Array representation (0-based and 1-based)

The two core repair operations

sift-up (percolate-up / bubble-up) - used after insertion, when a node may be *better* than its parent:

  1. Place the new key at the end of the array (index n, keeping the shape complete).
  2. While the node is better than its parent, swap with the parent.
  3. Stop at the root or when the property holds. Cost O(log n), at most ⌊log₂ n⌋ swaps.

sift-down (percolate-down / heapify / max-heapify) - used after deletion or during build, when a node may be *worse* than a child:

  1. Find the better of the two children (largest child for a max-heap).
  2. If that child is better than the node, swap and recurse/loop into the child's position.
  3. Stop at a leaf or when the property holds. Cost O(log n), but two comparisons per level (child-vs-child, then node-vs-child).

insert(x): append + sift-up → O(log n) worst case, O(1) amortised/average for random keys (most inserts stop within a level or two). extract-max (delete root): return root, move the last element to the root, reduce size by 1, sift-down → O(log n) always. increase-key / decrease-key(i, new): change the key, then sift-up (max-heap increase, min-heap decrease) or sift-down (the opposite direction) → O(log n). Needs the element's index. delete(i): replace with the last element, shrink, then sift-up or sift-down depending on comparison → O(log n). peek / find-max: O(1), read index 1 (or 0). build-heap: sift-down on indices ⌊n/2⌋ down to 1 → O(n), not O(n log n).

Why build-heap is O(n) - derivation outline

Heap sort

HEAPSORT(A, n):            # 1-based, ascending output using a MAX-heap
  BUILD-MAX-HEAP(A, n)     # O(n)
  for i = n down to 2:
      swap A[1], A[i]      # largest goes to its final place
      n' = i - 1
      MAX-HEAPIFY(A, 1, n')# O(log i)

Variants: d-ary, binomial, Fibonacci, and other priority-queue implementations

Implementationinsertfind-minextract-mindecrease-keymerge
Unsorted array/listO(1)O(n)O(n)O(1)O(1)
Sorted arrayO(n)O(1)O(1)O(n)O(n)
Sorted linked listO(n)O(1)O(1)O(n)O(n)
Binary heapO(log n)O(1)O(log n)O(log n)O(n)
d-ary heapO(log_d n)O(1)O(d log_d n)O(log_d n)O(n)
Binomial heapO(log n)O(log n)*O(log n)O(log n)O(log n)
Fibonacci heapO(1)O(1)O(log n) am.O(1) am.O(1)
Balanced BSTO(log n)O(log n)O(log n)O(log n)O(n)

\*O(1) if a pointer to the minimum root is cached.

Library support and standard patterns

Key points

Formulas and examples

parent(i) = ⌊i/2⌋, left(i) = 2i, right(i) = 2i + 1 (1-based); parent(i) = ⌊(i−1)/2⌋, left(i) = 2i + 1, right(i) = 2i + 2 (0-based)
Height of an n-node heap h = ⌊log₂ n⌋; max nodes in a heap of height h = 2h+1 − 1; min nodes = 2h
Number of leaves = ⌈n/2⌉; number of internal nodes = ⌊n/2⌋; nodes at height h ≤ ⌈n / 2h+1⌉
Build-heap cost T(n) = Σ(h=0..log n) ⌈n/2h+1⌉ · O(h) = O(n), using Σ(h≥0) h/2h = 2
Heap-sort time = O(n) [build] + Σ(i=2..n) O(log i) = Θ(n log n); auxiliary space = O(1)
d-ary heap: height = ⌈log_d(n(d−1)+1)⌉ − 1 = Θ(log_d n); insert = O(log_d n); extract-min = O(d · log_d n)
Dijkstra with a binary heap = O((V + E) log V); with a Fibonacci heap = O(E + V log V)
k largest of n: time O(n log k), space O(k) (size-k min-heap) versus O(n log n) by sorting

Example 1 - build a max-heap bottom-up from A = [4, 10, 3, 5, 1, 8, 2] (1-based, n = 7).

Example 2 - insert 15, then extract-max from [10, 5, 8, 4, 1, 3, 2].

Example 3 - heap sort trace on [10, 5, 8, 4] (max-heap already valid, ascending output).

StepActionArraySorted tail
1swap A[1]↔A[4], n'=3, heapify[8, 5, 4 \10]10
2swap A[1]↔A[3], n'=2, heapify[5, 4 \8, 10]8, 10
3swap A[1]↔A[2], n'=1[4 \5, 8, 10]5, 8, 10

Example 4 - 3rd largest of [7, 10, 4, 3, 20, 15] with a size-3 min-heap.

Example 5 - running median of the stream 5, 15, 1, 3 using two heaps.

Example 6 - stable / tie-broken priority queue. Push the tuple (priority, counter, payload) with a monotonically increasing counter. Equal priorities then compare by insertion order, making the queue FIFO within a priority class - the standard fix in Python heapq and Java comparators, and also prevents TypeError when payloads are not comparable.

import heapq
h, seq = [], 0
for pr, task in [(2,"backup"), (1,"alert"), (2,"report")]:
    heapq.heappush(h, (pr, seq, task)); seq += 1
while h:
    print(heapq.heappop(h)[2])   # alert, backup, report

Where it is used

Interview questions

Common mistakes

Related topics

Trees and binary search trees · Balanced trees: AVL, red-black and B-trees · Graphs and their representation · Sorting algorithms and their analysis · Hashing and hash tables · Greedy algorithms (Huffman, Dijkstra, Prim)

Open in the Knowledge reader →Find jobs →