Heaps & priority queues

A heap keeps a list just ordered enough to hand out the smallest item at any moment, at O(log n) per add or remove. It is how a program always works on whatever is most urgent next.

3 examples17 to practiceRead · 6 min
The idea

A priority queue always hands out the most urgent item next

A queue serves items in the order they arrived. A priority queue serves them by priority instead: you can add an item at any time, and taking one out always gives the smallest (or, if you choose, the largest) of those still waiting. Think of a hospital's waiting list, a timer system running whichever timer is due soonest, or a search that always extends its cheapest path so far.

The obvious ways to build one are each slow at one of the two operations:

Built on

Add an item

Take the smallest

Look at the smallest

An unsorted list

O(1)

O(n): scan for it

O(n)

A sorted list, largest first (take from the end)

O(n): shift to make room

O(1)

O(1)

A binary heap

O(log n)

O(log n)

O(1)

A heap keeps the list only partly in order: enough to know the smallest item, and cheap to repair after each change.

The shape

A heap is a complete tree stored in a plain list

A complete binary tree is a binary tree where every level is full except possibly the last, and the last level is filled from the left. Such a tree needs no references at all: number the nodes level by level, left to right, and store them in a list in that order. For the item at index i, the children are at 2i + 1 and 2i + 2, and the parent is at floor((i − 1) / 2).

A binary heap is a complete binary tree with one more rule, the heap property. In a min-heap, every parent is no larger than its children, so the smallest value is always the root, heap[0]. A max-heap is the mirror image: every parent is no smaller than its children, and the largest value is at the root.

The rule

Every parent is no larger than its children. That's all a min-heap promises: the smallest item is at index 0, and nothing is said about the order of siblings or of different branches.

So a heap is not sorted. In the min-heap [1, 3, 6, 5, 9, 8, 7], 6 sits above 8 and 7, but 5, which is smaller, sits on a lower level of the other branch. Each change only has to repair one path between the root and the bottom, and the tree has about log₂ n levels.

Basic

Push puts the new item at the end and lets it climb

Appending keeps the tree complete, but the new value may be smaller than its parent. Swap it with its parent while that is so. This is called sift up. It stops at the first parent that is no larger, or at the root.

Medium

Pop takes the root, moves the last item up, and lets it sink

The smallest item is the root, but removing it leaves a hole at the top. Fill it with the last item, which keeps the tree complete, and then repair the order: while the moved item is larger than one of its children, swap it with the smaller child. That is sift down. Choosing the smaller child matters: it is the only one that can sit above the other child.

Hard

Heapify builds a whole heap in linear time

Pushing n items one by one costs O(n log n). Heapify does better: start from the list as it is, and sift down every item that has children, from the last such item back to the root. Each sift down only has to fix one subtree, and the subtrees below it are already heaps by then.

Half the items are at the bottom and never move, a quarter can sink at most one level, an eighth at most two, and so on. Adding that up gives fewer than n swaps in total: heapify is O(n).

The walkthrough heapifies a list of stone piles into a max-heap, then plays a game that needs the largest value over and over: halve the biggest pile, k times. Each turn shrinks the root in place and sifts it down, one O(log n) step instead of a pop and a push.

In practice

Use the library heap, and know which way it faces

Language

Built in

Faces

Python

heapq functions on a plain list

min-heap; negate values for a max-heap

Java

PriorityQueue

min-heap by default; pass a comparator for a max-heap

C++

std::priority_queue

max-heap by default

JavaScript

none

write a small one, or use a library

Library heaps can't change an item's priority in place. The usual workaround is to push the item again with its new priority and, when an outdated copy comes out of the heap later, skip it.

Heapify a list into a max-heap and pop everything, putting each popped item in the slot freed at the end, and the list ends up sorted. That is heap sort: O(n log n) in the worst case and in place, but not stable, and in practice usually slower than quicksort.

Check yourself

4 questions

1. In a min-heap stored in a list, where is the smallest value?
2. Is the min-heap [1, 3, 2, 7, 4] sorted?
3. When sifting down, why swap with the smaller child?
4. Why is heapify O(n) rather than O(n log n)?
Takeaways

Remember this

  • A priority queue hands out the smallest (or largest) item next; a binary heap gives O(log n) add and remove, and O(1) look.
  • A binary heap is a complete binary tree in a list: children of i at 2i + 1 and 2i + 2, parent at floor((i − 1) / 2).
  • The heap property only orders parents before children, which is cheap to repair: sift up after a push, sift down after a pop.
  • Heapify builds a heap from a list in O(n) by sifting down from the last parent back to the root.
  • Library heaps differ in direction: Python and Java default to a min-heap, C++ to a max-heap.
Practice

Now solve some yourself

Only this topic 0 of 9 solved

  1. Easy
    Repeatedly take from the richest pile
    Heaps & priority queues
  2. Easy
    Smash the two heaviest stones
    Heaps & priority queues
  3. Medium
    The smallest number not yet taken
    Heaps & priority queues
  4. Medium
    Cheapest shopping with discount tickets
    Heaps & priority queues
  5. Medium
    Hand out the lowest free seat
    Heaps & priority queues
  6. Medium
    Score by taking the largest, then shrinking it
    Heaps & priority queues
  7. Medium
    Fewest halvings to cut a total in half
    Heaps & priority queues
  8. Medium
    Add students where the pass ratio gains most
    Heaps & priority queues
  9. Medium
    Run tasks in the order a single processor would
    Heaps & priority queues

Mixed with topics you have practiced 0 unlocked

    8 more once you have practiced Greedy algorithms, Basic sorting, Merge intervals, Hash tables & maps. Play one of its examples to the end and solve one of its problems to unlock them.

    Problems open on their own sites (LeetCode, Codeforces, CodeChef, AtCoder, CSES) in a new tab. Tick Solved when you get it accepted there; your ticks decide which mixed problems open next.

    Next

    Top-K & k-way merge

    Continue