Heaps · Heap Fundamentals

Heap Implementation From Scratch

Implementing insert and extract-min directly on a plain array using parent/child index arithmetic - the mechanics PriorityQueue normally hides.

  • 5 min read
  • 7 practice questions
  • 1 graded problem
On this page
  1. Description
  2. The approach
  3. A second flavor: build in O(n), sort in place
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

PriorityQueue hides a small, elegant data structure. Knowing how it works explains its costs (why peek is O(1) and remove(Object) is O(n)), why iterating it isn't sorted, and how to build variants the library lacks, such as a heap with decrease-key.

This skill builds a binary heap by hand: the array layout and its index arithmetic, sift-up and sift-down, bottom-up heapify in O(n), and heapsort.

We'll go from simple to more technical:

  1. The pattern: an array as a complete tree, insert with sift-up, extract with sift-down.
  2. A second flavor: build a heap in O(n) from the bottom up, then heapsort in place.
  3. Where binary heaps run (priority queues, schedulers, Dijkstra, simulations).
  4. What breaks it: the wrong child, holes, treating the array as sorted, empty values.

The approach

Java

int[] heap = new int[capacity];
int size = 0;

void insert(int val) {
    int i = size++;
    heap[i] = val;                                        // append at the end
    while (i > 0 && heap[(i - 1) / 2] > heap[i]) {        // sift up while smaller than the parent
        swap(i, (i - 1) / 2);
        i = (i - 1) / 2;
    }
}

int extractMin() {
    if (size == 0) return -1;
    int min = heap[0];
    heap[0] = heap[--size];                               // move the last element to the root
    int i = 0;
    while (true) {                                        // sift down toward the SMALLER child
        int left = 2 * i + 1, right = left + 1, smallest = i;
        if (left < size && heap[left] < heap[smallest]) smallest = left;
        if (right < size && heap[right] < heap[smallest]) smallest = right;
        if (smallest == i) break;
        swap(i, smallest);
        i = smallest;
    }
    return min;
}

Python

class MinHeap:
    def __init__(self):
        self.heap = []

    def insert(self, val: int) -> None:
        heap = self.heap
        heap.append(val)                                     # append at the end
        i = len(heap) - 1
        while i > 0 and heap[(i - 1) // 2] > heap[i]:        # sift up while smaller than the parent
            heap[i], heap[(i - 1) // 2] = heap[(i - 1) // 2], heap[i]
            i = (i - 1) // 2

    def extract_min(self) -> int:
        heap = self.heap
        if not heap:
            return -1
        smallest = heap[0]
        last = heap.pop()
        if heap:
            heap[0] = last                                   # move the last element to the root
            i = 0
            while True:                                      # sift down toward the SMALLER child
                left, right, best = 2 * i + 1, 2 * i + 2, i
                if left < len(heap) and heap[left] < heap[best]:
                    best = left
                if right < len(heap) and heap[right] < heap[best]:
                    best = right
                if best == i:
                    break
                heap[i], heap[best] = heap[best], heap[i]
                i = best
        return smallest

The array is the tree: a complete binary tree stored level by level, so node i's children are 2i + 1 and 2i + 2 and its parent is (i - 1) / 2 - node 5's parent is 2, its children 11 and 12. Completeness (no gaps) is what makes the index arithmetic work, and it's why extract moves the last element to the root. Sifting down must swap with the smaller child: swapping with the left child only puts a larger value above a smaller one.

A second flavor: build in O(n), sort in place

Turning an existing array into a heap by inserting one at a time costs O(n log n). Sifting down from the last parent back to the root costs O(n):

Java

for (int i = n / 2 - 1; i >= 0; i--) siftDown(a, i, n);      // heapify bottom-up

Python

for i in range(n // 2 - 1, -1, -1):                          # heapify bottom-up
    sift_down(a, i, n)

# heapq.heapify(a) does exactly this, in place, in O(n)

Half the nodes are leaves (no work), a quarter sift at most one level, an eighth two levels, and so on - the total is under 2n swaps. For 2^20 values it made about 1.05 million swaps, against 21 million for n log n.

Heapsort builds a max-heap this way, then repeatedly swaps the root (the maximum) to the end and sifts down the shrunken heap: O(n log n) worst case, O(1) extra space, not stable.

Where you'll see it

java.util.PriorityQueue is exactly this array heap (with a comparator); OS schedulers and timer wheels, Dijkstra's and Prim's algorithms, and event-driven simulations all run on binary heaps. Knowing that iteration isn't sorted - new PriorityQueue<>(List.of(5, 1, 4, 2, 3)) iterates as [1, 2, 4, 5, 3] - avoids a classic bug when printing or streaming a queue's contents. Python's heapq is this same array heap on a plain list: h = [5, 1, 4, 2, 3]; heapq.heapify(h) leaves [1, 2, 4, 5, 3] - a valid heap, not a sorted list.

Which tool for which job?

You needUse
Repeated min (or max) with insertsa binary heap
A heap from an existing arraybottom-up heapify, O(n)
Sorting in place with an O(n log n) guaranteeheapsort
Decrease-key (Dijkstra, Prim)an indexed heap, or lazy deletion
Equal priorities in arrival order(priority, sequence number) entries
Next larger than x, or in-order iterationa balanced BST (TreeMap), not a heap

Common mistakes

MistakeWhat happensDo this instead
Sifting toward the left child onlya larger value ends up above a smaller onecompare both children, swap with the smaller
Shifting everything up on extractO(n), and the tree shape breaksmove the last element to the root, sift down
Treating the array as sortediteration order looks randomonly the root is known
-1 as "empty"a stored -1 looks like an empty heapthrow, or return an Optional or None
Building by n insertsO(n log n) in the worst caseheapify bottom-up

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. In a 0-based array heap, what are the parent and children of index 5?

Show answer

Parent (5 - 1) / 2 = 2; children 2 * 5 + 1 = 11 and 2 * 5 + 2 = 12.

2. Why does extract move the last element to the root instead of shifting everything up?

Show answer

The heap must stay a complete tree with no gaps, so the only position that can disappear is the last one. Moving that element to the root keeps the shape; sifting it down restores the order - O(log n) instead of O(n).

3. Why is bottom-up heapify O(n) rather than O(n log n)?

Show answer

Most nodes are near the bottom and sift down only a level or two: half are leaves, a quarter move at most one level, and so on. The sum is below 2n - measured at about n swaps for 2^20 values.

Key takeaways

  • Heap = complete binary tree in an array: children 2i + 1, 2i + 2; parent (i - 1) / 2.
  • Insert: append and sift up. Extract: move the last element to the root and sift down to the smaller child.
  • Build bottom-up in O(n); heapsort is O(n log n), in place, not stable.
  • Iterating a PriorityQueue isn't sorted; remove(Object) is O(n); there's no decrease-key.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Repeated min with insertsbinary heap
Heap from an arrayheapify, O(n)
In-place O(n log n) sortheapsort
Decrease-keyindexed heap, or lazy deletion
FIFO among ties(priority, seq)
Ordered queriesTreeMap, not a heap

Snippets worth knowing by heart

Java

// parent (i - 1) / 2, children 2i + 1 and 2i + 2
for (int i = n / 2 - 1; i >= 0; i--) siftDown(a, i, n);        // heapify in O(n)
PriorityQueue<Task> q = new PriorityQueue<>(
    Comparator.comparingInt(Task::priority).thenComparingLong(Task::seq));   // FIFO among ties

Python

import heapq, itertools
counter = itertools.count()
heapq.heappush(q, (priority, next(counter), task))      # never compares tasks

The traps

  • The left child only: sifting down toward the left leaves a larger value above a smaller one. Swap with the smaller child.
  • Holes on extract: shifting everything up is O(n) and breaks the shape. Move the last element to the root, then sift down.
  • Sorted iteration that isn't: only the root is known; the rest looks random. Pop repeatedly for sorted order.
  • -1 as empty: a stored -1 looks like an empty heap. Throw, or return an Optional or None.
  • n inserts instead of heapify: building costs O(n log n). Heapify bottom-up in O(n).

Staff-level points, one line each

  • 2^20 keys: heapify 1,972,741 comparisons; one-by-one inserts 2,390,447 in random order but 18,874,390 in descending order.
  • heapq.heappop sinks the hole to a leaf and sifts up: 717,313 comparisons against 1,315,703 for 50,000 pops.
  • 2-, 4- and 8-ary heaps: 1.43M, 1.46M and 1.95M comparisons; 4-ary has half the levels.
  • Dijkstra (2,000 vertices, 40,000 edges): lazy heap peaked at 3,688 entries, indexed at 1,589, same distances.
  • Equal priorities pop in payload order, or raise TypeError for dicts; add a counter.

Say it in an interview

  • "The array is a complete tree: children at 2i + 1 and 2i + 2."
  • "Building from existing data is O(n) bottom-up."
  • "Java's PriorityQueue has no decrease-key, so I'd push duplicates and skip stale entries."
  • "For FIFO among equal priorities I add a sequence number."

Practice

This skill comes with 7 multiple-choice questions that tell you which misconception a wrong answer reveals and 1 graded problem. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Heap Implementation From Scratch free

Implement a Min Heap

HardProCoding problemJava · Python

Implement a min-heap from scratch (no java.util.PriorityQueue, and in Python no heapq), backed by a plain array, supporting:

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Heap Implementation From Scratch free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime