Heaps and Priority Queues

A priority queue maintains a collection of elements, each with a priority. The defining operations: insert an element, and extract the highest-priority element.

The most common implementation is the heap. Heaps power Dijkstra's algorithm, event-driven simulation, scheduling, and many ranking systems.

The priority queue interface

Standard operations:

- **insert(value, priority)**: add element

- **extractMin()** (or extractMax): remove and return highest-priority

- **peek()**: look at highest-priority without removing

- **decreaseKey()** (sometimes): change an element's priority

Optional:

- **remove(element)**: extract a specific element

- **merge(other)**: combine two priority queues

Binary heap

The standard implementation. Complete binary tree with the heap property:

- **Min-heap**: parent ≤ children

- **Max-heap**: parent ≥ children

Array representation

Stored in an array. Parent at i, children at 2i+1 and 2i+2.

No pointers. Cache-friendly. Classic.

Operations

- **Insert**: add at end; bubble up. O(log n).

- **Extract**: take root; move last to root; bubble down. O(log n).

- **Peek**: O(1).

- **Build heap from array**: O(n) (not O(n log n)).

- **Decrease key**: requires knowing position; O(log n).

When to use

Default priority queue. Simple, fast, cache-friendly.

Java's PriorityQueue, Python's heapq, C++'s std::priority_queue are binary heaps.

D-ary heap

Like binary heap but each node has d children.

Tradeoff:

- Larger d: shallower tree, faster extracts (less bubble up)

- Smaller d: faster decrease-key

For Dijkstra, d = 4 or 8 is sometimes optimal.

Binomial heap

Collection of binomial trees. Supports merge in O(log n).

Operations

- Insert: O(log n) worst, O(1) amortized

- Extract: O(log n)

- Merge: O(log n)

- Decrease key: O(log n)

Useful when frequent merging is needed.

Fibonacci heap

Theoretical workhorse. Supports decrease-key in amortized O(1).

Operations

- Insert: O(1) amortized

- Decrease key: O(1) amortized

- Extract: O(log n) amortized

Fibonacci heaps make Dijkstra theoretically O(m + n log n) instead of O((m + n) log n).

In practice: high constant factors. Binary heaps usually win for typical inputs.

Pairing heap

Simpler than Fibonacci; competitive in practice.

- Insert: O(1)

- Extract: O(log n) amortized

- Decrease key: O(log log n) amortized (open question)

Often the fastest in practice for graph algorithms.

Specialized heaps

Indexed priority queue

Maps elements to their position in the heap. Enables decrease-key.

Used in Dijkstra implementations.

Bounded priority queue

Fixed-size; eviction on overflow.

Used for top-K queries.

Soft heap

Allows incorrect results; faster bounds. Niche use.

Common applications

Dijkstra's algorithm

Find shortest paths from source. Priority queue holds frontier.

With binary heap: O((m + n) log n).

With Fibonacci heap: O(m + n log n).

Prim's MST algorithm

Similar structure to Dijkstra.

Heapsort

Sort by inserting all elements then extracting all.

O(n log n) worst case. In-place. Not stable.

Less common than introsort or quicksort in practice.

Event-driven simulation

Events have scheduled times. Priority queue extracts next event.

Discrete-event simulation, networking simulators, game engines.

Scheduling

Tasks have priorities. Scheduler extracts highest priority.

OS process schedulers, thread pools, job queues.

Top-K queries

Find K largest/smallest. Use heap of size K.

For K=1: just track max. For K small: heap is efficient.

For very large K, sort might be better.

Median maintenance

Two heaps: max-heap for lower half, min-heap for upper half. Median is at the boundary.

A* search

Like Dijkstra but with heuristic. Priority = cost so far + heuristic.

Huffman coding

Build optimal prefix code by repeatedly merging two least-frequent nodes.

Design considerations

Min vs max

Min-heap and max-heap have identical operations on negated priorities.

Some libraries support both; some only one.

Stability

Equal-priority elements: which extracts first?

Not stable by default. Add insertion-order tiebreaker if needed.

Mutability

If priorities change, you need decrease-key. Not all heaps support efficiently.

Concurrency

Concurrent heaps are tricky. Lock-free implementations exist but complex.

Common failure patterns

Wrong direction (min vs max)

Subtle bug; results look almost right.

Not handling stability

Equal-priority elements come out in surprising order.

Mutating elements after insert

If priority depends on mutable state, heap invariant breaks.

Heap when sorted array would do

If you're extracting all elements, sort + iterate may be simpler.

Implementing your own

Standard library implementations are well-tested. Use them.

Performance on small N

For small heaps, sorted array is faster (cache effects).

Performance characteristics

For binary heap with N elements:

- Insert: ~log₂ N comparisons

- Extract: ~log₂ N comparisons + 1 swap

For N = 1M: ~20 comparisons per operation. Fast.

Modern CPUs: ~50 nanoseconds per operation.

Implementation tips

For high-performance:

- Inline everything

- Avoid pointer indirection

- Cache priority adjacent to value

- Consider d-ary for shallow tree

For correctness:

- Test the heap property explicitly

- Random testing with verification

- Benchmark realistic workloads

Standard library guidance

- **Python**: heapq (min-heap; min-only operations)

- **Java**: PriorityQueue (min-heap by default; comparator-based)

- **C++**: std::priority_queue (max-heap by default)

- **Rust**: std::collections::BinaryHeap (max-heap)

Most are binary heaps. For specialized needs (decrease-key, merge), check available libraries or implement.

When priority queue isn't right

If you need:

- Range queries → use balanced BST

- Sorted iteration → sort once

- Just maximum (and elements never become irrelevant) → track running max

- O(1) extract regardless of size → consider buckets if priorities are bounded integers

Further Reading

- [BalancedSearchTrees](BalancedSearchTrees) — Different access pattern

- [HashTableDesign](HashTableDesign) — Different access pattern

- [Data Structures Hub](DataStructuresHub) — Cluster index