Heap operations
The heap operations: sift-up, sift-down, insert, peek, extract, replace the root and building a heap in O(n).
Learning goals
- Restore heap order with sift-up and sift-down
- Implement insert, peek and extract on an array
- Use replace-root and decrease-key where they save work
- Build a heap bottom-up in O(n) and explain why it beats repeated insertion
Prerequisites
Every heap operation is the same repair: put an element in the wrong place and let it slide along one root-to-leaf path until order is restored.
Sift up
**Sift-up** (also called bubble-up or percolate-up) moves a node toward the root while it is smaller than its parent: compare with the parent, swap if smaller, and continue from the parent's position. Each step moves one level, so the cost is at most the height, O(log n), and often much less. In the “Insert 2 (sift-up)” preset the new value 2 swaps twice, taking 3 comparisons.
Heap order below the moving node is never disturbed: the displaced parent is no larger than the old subtree, because it was above it already.
1static void siftUp(int[] a, int i) {2while (i > 0) {3int p = (i - 1) / 2; // parent4if (a[p] <= a[i]) break; // heap order restored5swap(a, p, i);6i = p;7}8}
Best
O(1)
Average
O(1)
Worst
O(log n)
Space
O(1)
Binary heap: sift-up, sift-down, insert, extract, replace, decrease-key and build-heap
Interactive · edit the inputmin-heap as a tree · 8 nodes
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
heap array (index = position in the tree)
Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root.
1static void siftUp(int[] a, int i) {2while (i > 0) {3int p = (i - 1) / 2; // parent4if (a[p] <= a[i]) break; // heap order restored5swap(a, p, i);6i = p;7}8}9static void siftDown(int[] a, int n, int i) {10while (true) {11int l = 2 * i + 1, r = l + 1, m = i;12if (l < n && a[l] < a[m]) m = l; // find the smallest of the node and its children13if (r < n && a[r] < a[m]) m = r;14if (m == i) break; // both children are larger: done15swap(a, i, m);16i = m;17}18}19static int size;20static void insert(int[] a, int x) { a[size] = x; siftUp(a, size++); }21static int extractMin(int[] a) {22int min = a[0];23a[0] = a[--size]; // move the last leaf to the root24siftDown(a, size, 0);25return min;26}27static void buildHeap(int[] a, int n) {28for (int i = n / 2 - 1; i >= 0; i--) siftDown(a, n, i); // the last n/2 entries are leaves already29}30static int replaceRoot(int[] a, int x) { // pop and push in one sift31int min = a[0];32a[0] = x;33siftDown(a, size, 0);34return min;35}36static void decreaseKey(int[] a, int i, int x) { a[i] = x; siftUp(a, i); }
Live state
size
8
Execution trace (dry run table)Show 24 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | size=8 | Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root. |
| 2 | L28 | size=8 index=3 | Sift down the node at index 3 (3). Everything below it is already a heap. |
| 3 | L12 | size=8 node=3 | Compare 3 with its only child 1. The smaller child is 1. |
| 4 | L15 | size=8 node=3 | Swap 3 with 1: the smaller value moves up. |
| 5 | L12 | size=8 node=3 | Compare 3 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 6 | L28 | size=8 index=2 | Sift down the node at index 2 (8). Everything below it is already a heap. |
| 7 | L12 | size=8 node=8 | Compare 8 with its children 2 and 6. The smaller child is 2. |
| 8 | L15 | size=8 node=8 | Swap 8 with 2: the smaller value moves up. |
| 9 | L12 | size=8 node=8 | Compare 8 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 10 | L28 | size=8 index=1 | Sift down the node at index 1 (7). Everything below it is already a heap. |
| 11 | L12 | size=8 node=7 | Compare 7 with its children 1 and 5. The smaller child is 1. |
| 12 | L15 | size=8 node=7 | Swap 7 with 1: the smaller value moves up. |
| 13 | L12 | size=8 node=7 | Compare 7 with its only child 3. The smaller child is 3. |
| 14 | L15 | size=8 node=7 | Swap 7 with 3: the smaller value moves up. |
| 15 | L12 | size=8 node=7 | Compare 7 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 16 | L28 | size=8 index=0 | Sift down the node at index 0 (9). Everything below it is already a heap. |
| 17 | L12 | size=8 node=9 | Compare 9 with its children 1 and 2. The smaller child is 1. |
| 18 | L15 | size=8 node=9 | Swap 9 with 1: the smaller value moves up. |
| 19 | L12 | size=8 node=9 | Compare 9 with its children 3 and 5. The smaller child is 3. |
| 20 | L15 | size=8 node=9 | Swap 9 with 3: the smaller value moves up. |
| 21 | L12 | size=8 node=9 | Compare 9 with its only child 7. The smaller child is 7. |
| 22 | L15 | size=8 node=9 | Swap 9 with 7: the smaller value moves up. |
| 23 | L12 | size=8 node=9 | Compare 9 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 24 | L1 | size=8 heap order=holds | [1, 3, 2, 7, 5, 8, 6, 9]: heap order holds. 11 comparisons and 7 swaps. |
Sift down
**Sift-down** moves a node toward the leaves while a child is smaller: find the smaller of the (up to two) children, swap if it is smaller than the node, and continue from there. It needs up to two comparisons per level, so up to 2 log2 n in the worst case. Choose the **smaller** child, or the other child would end up above a smaller sibling.
In the “Extract the minimum (sift-down)” preset the last leaf 6 moves to the root of [1, 3, 5, 7, 9, 8, 6] and sinks one level, using 4 comparisons in all, ending in [3, 6, 5, 7, 9, 8].
1static void siftDown(int[] a, int n, int i) {2while (true) {3int l = 2 * i + 1, r = l + 1, m = i;4if (l < n && a[l] < a[m]) m = l; // find the smallest of the node and its children5if (r < n && a[r] < a[m]) m = r;6if (m == i) break; // both children are larger: done7swap(a, i, m);8i = m;9}10}
Best
O(1)
Average
O(log n)
Worst
O(log n)
Space
O(1)
Binary heap: sift-up, sift-down, insert, extract, replace, decrease-key and build-heap
Interactive · edit the inputmin-heap as a tree · 8 nodes
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
heap array (index = position in the tree)
Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root.
1static void siftUp(int[] a, int i) {2while (i > 0) {3int p = (i - 1) / 2; // parent4if (a[p] <= a[i]) break; // heap order restored5swap(a, p, i);6i = p;7}8}9static void siftDown(int[] a, int n, int i) {10while (true) {11int l = 2 * i + 1, r = l + 1, m = i;12if (l < n && a[l] < a[m]) m = l; // find the smallest of the node and its children13if (r < n && a[r] < a[m]) m = r;14if (m == i) break; // both children are larger: done15swap(a, i, m);16i = m;17}18}19static int size;20static void insert(int[] a, int x) { a[size] = x; siftUp(a, size++); }21static int extractMin(int[] a) {22int min = a[0];23a[0] = a[--size]; // move the last leaf to the root24siftDown(a, size, 0);25return min;26}27static void buildHeap(int[] a, int n) {28for (int i = n / 2 - 1; i >= 0; i--) siftDown(a, n, i); // the last n/2 entries are leaves already29}30static int replaceRoot(int[] a, int x) { // pop and push in one sift31int min = a[0];32a[0] = x;33siftDown(a, size, 0);34return min;35}36static void decreaseKey(int[] a, int i, int x) { a[i] = x; siftUp(a, i); }
Live state
size
8
Execution trace (dry run table)Show 24 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | size=8 | Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root. |
| 2 | L28 | size=8 index=3 | Sift down the node at index 3 (3). Everything below it is already a heap. |
| 3 | L12 | size=8 node=3 | Compare 3 with its only child 1. The smaller child is 1. |
| 4 | L15 | size=8 node=3 | Swap 3 with 1: the smaller value moves up. |
| 5 | L12 | size=8 node=3 | Compare 3 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 6 | L28 | size=8 index=2 | Sift down the node at index 2 (8). Everything below it is already a heap. |
| 7 | L12 | size=8 node=8 | Compare 8 with its children 2 and 6. The smaller child is 2. |
| 8 | L15 | size=8 node=8 | Swap 8 with 2: the smaller value moves up. |
| 9 | L12 | size=8 node=8 | Compare 8 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 10 | L28 | size=8 index=1 | Sift down the node at index 1 (7). Everything below it is already a heap. |
| 11 | L12 | size=8 node=7 | Compare 7 with its children 1 and 5. The smaller child is 1. |
| 12 | L15 | size=8 node=7 | Swap 7 with 1: the smaller value moves up. |
| 13 | L12 | size=8 node=7 | Compare 7 with its only child 3. The smaller child is 3. |
| 14 | L15 | size=8 node=7 | Swap 7 with 3: the smaller value moves up. |
| 15 | L12 | size=8 node=7 | Compare 7 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 16 | L28 | size=8 index=0 | Sift down the node at index 0 (9). Everything below it is already a heap. |
| 17 | L12 | size=8 node=9 | Compare 9 with its children 1 and 2. The smaller child is 1. |
| 18 | L15 | size=8 node=9 | Swap 9 with 1: the smaller value moves up. |
| 19 | L12 | size=8 node=9 | Compare 9 with its children 3 and 5. The smaller child is 3. |
| 20 | L15 | size=8 node=9 | Swap 9 with 3: the smaller value moves up. |
| 21 | L12 | size=8 node=9 | Compare 9 with its only child 7. The smaller child is 7. |
| 22 | L15 | size=8 node=9 | Swap 9 with 7: the smaller value moves up. |
| 23 | L12 | size=8 node=9 | Compare 9 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 24 | L1 | size=8 heap order=holds | [1, 3, 2, 7, 5, 8, 6, 9]: heap order holds. 11 comparisons and 7 swaps. |
Insert
To insert, place the new value in the first free slot (index size), which keeps the shape complete, and sift it up. The cost is O(log n) worst case, but on average only O(1) swaps for random data, because most elements belong near the bottom. In the animation's “Build by insertion” presets you can compare the effect on sorted and reverse-sorted input.
When the array is full, grow it by doubling, as for any dynamic array; that is the amortised O(1) cost of the array on top of the heap's own O(log n).
1static int size;2static void insert(int[] a, int x) {3a[size] = x; // the first free slot keeps the tree complete4siftUp(a, size++);5}6static void siftUp(int[] a, int i) {7while (i > 0 && a[(i - 1) / 2] > a[i]) { swap(a, (i - 1) / 2, i); i = (i - 1) / 2; }8}
Best
O(1)
Average
O(1)
Worst
O(log n)
Space
O(1)
Binary heap: sift-up, sift-down, insert, extract, replace, decrease-key and build-heap
Interactive · edit the inputmin-heap as a tree · 8 nodes
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
heap array (index = position in the tree)
Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root.
1static void siftUp(int[] a, int i) {2while (i > 0) {3int p = (i - 1) / 2; // parent4if (a[p] <= a[i]) break; // heap order restored5swap(a, p, i);6i = p;7}8}9static void siftDown(int[] a, int n, int i) {10while (true) {11int l = 2 * i + 1, r = l + 1, m = i;12if (l < n && a[l] < a[m]) m = l; // find the smallest of the node and its children13if (r < n && a[r] < a[m]) m = r;14if (m == i) break; // both children are larger: done15swap(a, i, m);16i = m;17}18}19static int size;20static void insert(int[] a, int x) { a[size] = x; siftUp(a, size++); }21static int extractMin(int[] a) {22int min = a[0];23a[0] = a[--size]; // move the last leaf to the root24siftDown(a, size, 0);25return min;26}27static void buildHeap(int[] a, int n) {28for (int i = n / 2 - 1; i >= 0; i--) siftDown(a, n, i); // the last n/2 entries are leaves already29}30static int replaceRoot(int[] a, int x) { // pop and push in one sift31int min = a[0];32a[0] = x;33siftDown(a, size, 0);34return min;35}36static void decreaseKey(int[] a, int i, int x) { a[i] = x; siftUp(a, i); }
Live state
size
8
Execution trace (dry run table)Show 24 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | size=8 | Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root. |
| 2 | L28 | size=8 index=3 | Sift down the node at index 3 (3). Everything below it is already a heap. |
| 3 | L12 | size=8 node=3 | Compare 3 with its only child 1. The smaller child is 1. |
| 4 | L15 | size=8 node=3 | Swap 3 with 1: the smaller value moves up. |
| 5 | L12 | size=8 node=3 | Compare 3 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 6 | L28 | size=8 index=2 | Sift down the node at index 2 (8). Everything below it is already a heap. |
| 7 | L12 | size=8 node=8 | Compare 8 with its children 2 and 6. The smaller child is 2. |
| 8 | L15 | size=8 node=8 | Swap 8 with 2: the smaller value moves up. |
| 9 | L12 | size=8 node=8 | Compare 8 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 10 | L28 | size=8 index=1 | Sift down the node at index 1 (7). Everything below it is already a heap. |
| 11 | L12 | size=8 node=7 | Compare 7 with its children 1 and 5. The smaller child is 1. |
| 12 | L15 | size=8 node=7 | Swap 7 with 1: the smaller value moves up. |
| 13 | L12 | size=8 node=7 | Compare 7 with its only child 3. The smaller child is 3. |
| 14 | L15 | size=8 node=7 | Swap 7 with 3: the smaller value moves up. |
| 15 | L12 | size=8 node=7 | Compare 7 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 16 | L28 | size=8 index=0 | Sift down the node at index 0 (9). Everything below it is already a heap. |
| 17 | L12 | size=8 node=9 | Compare 9 with its children 1 and 2. The smaller child is 1. |
| 18 | L15 | size=8 node=9 | Swap 9 with 1: the smaller value moves up. |
| 19 | L12 | size=8 node=9 | Compare 9 with its children 3 and 5. The smaller child is 3. |
| 20 | L15 | size=8 node=9 | Swap 9 with 3: the smaller value moves up. |
| 21 | L12 | size=8 node=9 | Compare 9 with its only child 7. The smaller child is 7. |
| 22 | L15 | size=8 node=9 | Swap 9 with 7: the smaller value moves up. |
| 23 | L12 | size=8 node=9 | Compare 9 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 24 | L1 | size=8 heap order=holds | [1, 3, 2, 7, 5, 8, 6, 9]: heap order holds. 11 comparisons and 7 swaps. |
Peek
peek returns the root, a[0], without changing anything: O(1). This is the whole point of the structure. Guard the empty case (return null or throw), since the array may hold stale values past size.
1static int size;2static int peek(int[] a) {3if (size == 0) throw new NoSuchElementException("empty heap");4return a[0];5}
Best
O(1)
Average
O(1)
Worst
O(1)
Space
O(1)
Extract-min/max
To extract the minimum, remember the root, move the **last** element to the root (this keeps the shape complete), shrink the size, and sift the new root down. The cost is O(log n). It is the only operation that needs sift-down on a single element, and extracting everything gives sorted order, which is heap sort.
The “Extract the minimum (sift-down)” preset shows [1, 3, 5, 7, 9, 8, 6] becoming [3, 6, 5, 7, 9, 8]; the heap shrank by one and the root changed from 1 to 3.
1static int size;2static int extractMin(int[] a) {3int min = a[0];4a[0] = a[--size]; // move the last leaf to the root5siftDown(a, size, 0);6return min;7}8static void siftDown(int[] a, int n, int i) {9while (true) {10int l = 2 * i + 1, r = l + 1, m = i;11if (l < n && a[l] < a[m]) m = l;12if (r < n && a[r] < a[m]) m = r;13if (m == i) return;14swap(a, i, m);15i = m;16}17}
Best
O(1)
Average
O(log n)
Worst
O(log n)
Space
O(1)
Binary heap: sift-up, sift-down, insert, extract, replace, decrease-key and build-heap
Interactive · edit the inputmin-heap as a tree · 8 nodes
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
heap array (index = position in the tree)
Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root.
1static void siftUp(int[] a, int i) {2while (i > 0) {3int p = (i - 1) / 2; // parent4if (a[p] <= a[i]) break; // heap order restored5swap(a, p, i);6i = p;7}8}9static void siftDown(int[] a, int n, int i) {10while (true) {11int l = 2 * i + 1, r = l + 1, m = i;12if (l < n && a[l] < a[m]) m = l; // find the smallest of the node and its children13if (r < n && a[r] < a[m]) m = r;14if (m == i) break; // both children are larger: done15swap(a, i, m);16i = m;17}18}19static int size;20static void insert(int[] a, int x) { a[size] = x; siftUp(a, size++); }21static int extractMin(int[] a) {22int min = a[0];23a[0] = a[--size]; // move the last leaf to the root24siftDown(a, size, 0);25return min;26}27static void buildHeap(int[] a, int n) {28for (int i = n / 2 - 1; i >= 0; i--) siftDown(a, n, i); // the last n/2 entries are leaves already29}30static int replaceRoot(int[] a, int x) { // pop and push in one sift31int min = a[0];32a[0] = x;33siftDown(a, size, 0);34return min;35}36static void decreaseKey(int[] a, int i, int x) { a[i] = x; siftUp(a, i); }
Live state
size
8
Execution trace (dry run table)Show 24 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | size=8 | Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root. |
| 2 | L28 | size=8 index=3 | Sift down the node at index 3 (3). Everything below it is already a heap. |
| 3 | L12 | size=8 node=3 | Compare 3 with its only child 1. The smaller child is 1. |
| 4 | L15 | size=8 node=3 | Swap 3 with 1: the smaller value moves up. |
| 5 | L12 | size=8 node=3 | Compare 3 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 6 | L28 | size=8 index=2 | Sift down the node at index 2 (8). Everything below it is already a heap. |
| 7 | L12 | size=8 node=8 | Compare 8 with its children 2 and 6. The smaller child is 2. |
| 8 | L15 | size=8 node=8 | Swap 8 with 2: the smaller value moves up. |
| 9 | L12 | size=8 node=8 | Compare 8 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 10 | L28 | size=8 index=1 | Sift down the node at index 1 (7). Everything below it is already a heap. |
| 11 | L12 | size=8 node=7 | Compare 7 with its children 1 and 5. The smaller child is 1. |
| 12 | L15 | size=8 node=7 | Swap 7 with 1: the smaller value moves up. |
| 13 | L12 | size=8 node=7 | Compare 7 with its only child 3. The smaller child is 3. |
| 14 | L15 | size=8 node=7 | Swap 7 with 3: the smaller value moves up. |
| 15 | L12 | size=8 node=7 | Compare 7 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 16 | L28 | size=8 index=0 | Sift down the node at index 0 (9). Everything below it is already a heap. |
| 17 | L12 | size=8 node=9 | Compare 9 with its children 1 and 2. The smaller child is 1. |
| 18 | L15 | size=8 node=9 | Swap 9 with 1: the smaller value moves up. |
| 19 | L12 | size=8 node=9 | Compare 9 with its children 3 and 5. The smaller child is 3. |
| 20 | L15 | size=8 node=9 | Swap 9 with 3: the smaller value moves up. |
| 21 | L12 | size=8 node=9 | Compare 9 with its only child 7. The smaller child is 7. |
| 22 | L15 | size=8 node=9 | Swap 9 with 7: the smaller value moves up. |
| 23 | L12 | size=8 node=9 | Compare 9 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 24 | L1 | size=8 heap order=holds | [1, 3, 2, 7, 5, 8, 6, 9]: heap order holds. 11 comparisons and 7 swaps. |
Replace root
**Replace root** pops the minimum and pushes a new value in a single sift-down: put the new value at the root and sink it. It does one pass of O(log n) where poll followed by add would do a sift-down and a sift-up. It is the right tool for keeping the k largest values (replace the smallest if the newcomer is larger) and for k-way merging (replace the popped head with the next element of its list).
The “Replace the root” preset replaces 1 with 4 in [1, 3, 5, 7, 9, 8, 6] and gets [3, 4, 5, 7, 9, 8, 6] after 4 comparisons and 1 swap.
1static int size;2static int replaceRoot(int[] a, int x) { // returns the old minimum3int min = a[0];4a[0] = x;5siftDown(a, size, 0); // one sift instead of an extract plus an insert6return min;7}8static void siftDown(int[] a, int n, int i) {9while (true) {10int l = 2 * i + 1, r = l + 1, m = i;11if (l < n && a[l] < a[m]) m = l;12if (r < n && a[r] < a[m]) m = r;13if (m == i) return;14swap(a, i, m);15i = m;16}17}
Best
O(1)
Average
O(log n)
Worst
O(log n)
Space
O(1)
Binary heap: sift-up, sift-down, insert, extract, replace, decrease-key and build-heap
Interactive · edit the inputmin-heap as a tree · 8 nodes
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
heap array (index = position in the tree)
Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root.
1static void siftUp(int[] a, int i) {2while (i > 0) {3int p = (i - 1) / 2; // parent4if (a[p] <= a[i]) break; // heap order restored5swap(a, p, i);6i = p;7}8}9static void siftDown(int[] a, int n, int i) {10while (true) {11int l = 2 * i + 1, r = l + 1, m = i;12if (l < n && a[l] < a[m]) m = l; // find the smallest of the node and its children13if (r < n && a[r] < a[m]) m = r;14if (m == i) break; // both children are larger: done15swap(a, i, m);16i = m;17}18}19static int size;20static void insert(int[] a, int x) { a[size] = x; siftUp(a, size++); }21static int extractMin(int[] a) {22int min = a[0];23a[0] = a[--size]; // move the last leaf to the root24siftDown(a, size, 0);25return min;26}27static void buildHeap(int[] a, int n) {28for (int i = n / 2 - 1; i >= 0; i--) siftDown(a, n, i); // the last n/2 entries are leaves already29}30static int replaceRoot(int[] a, int x) { // pop and push in one sift31int min = a[0];32a[0] = x;33siftDown(a, size, 0);34return min;35}36static void decreaseKey(int[] a, int i, int x) { a[i] = x; siftUp(a, i); }
Live state
size
8
Execution trace (dry run table)Show 24 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | size=8 | Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root. |
| 2 | L28 | size=8 index=3 | Sift down the node at index 3 (3). Everything below it is already a heap. |
| 3 | L12 | size=8 node=3 | Compare 3 with its only child 1. The smaller child is 1. |
| 4 | L15 | size=8 node=3 | Swap 3 with 1: the smaller value moves up. |
| 5 | L12 | size=8 node=3 | Compare 3 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 6 | L28 | size=8 index=2 | Sift down the node at index 2 (8). Everything below it is already a heap. |
| 7 | L12 | size=8 node=8 | Compare 8 with its children 2 and 6. The smaller child is 2. |
| 8 | L15 | size=8 node=8 | Swap 8 with 2: the smaller value moves up. |
| 9 | L12 | size=8 node=8 | Compare 8 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 10 | L28 | size=8 index=1 | Sift down the node at index 1 (7). Everything below it is already a heap. |
| 11 | L12 | size=8 node=7 | Compare 7 with its children 1 and 5. The smaller child is 1. |
| 12 | L15 | size=8 node=7 | Swap 7 with 1: the smaller value moves up. |
| 13 | L12 | size=8 node=7 | Compare 7 with its only child 3. The smaller child is 3. |
| 14 | L15 | size=8 node=7 | Swap 7 with 3: the smaller value moves up. |
| 15 | L12 | size=8 node=7 | Compare 7 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 16 | L28 | size=8 index=0 | Sift down the node at index 0 (9). Everything below it is already a heap. |
| 17 | L12 | size=8 node=9 | Compare 9 with its children 1 and 2. The smaller child is 1. |
| 18 | L15 | size=8 node=9 | Swap 9 with 1: the smaller value moves up. |
| 19 | L12 | size=8 node=9 | Compare 9 with its children 3 and 5. The smaller child is 3. |
| 20 | L15 | size=8 node=9 | Swap 9 with 3: the smaller value moves up. |
| 21 | L12 | size=8 node=9 | Compare 9 with its only child 7. The smaller child is 7. |
| 22 | L15 | size=8 node=9 | Swap 9 with 7: the smaller value moves up. |
| 23 | L12 | size=8 node=9 | Compare 9 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 24 | L1 | size=8 heap order=holds | [1, 3, 2, 7, 5, 8, 6, 9]: heap order holds. 11 comparisons and 7 swaps. |
Build-heap in O(n)
Turning an arbitrary array into a heap by n insertions costs O(n log n). **Floyd's bottom-up construction** does better: the last n / 2 elements are already one-element heaps, so go from the last internal node down to the root and sift each one down. Most nodes are near the bottom, where sift-down is cheap: about n / 2 nodes sift down 0 levels, n / 4 sink at most 1, n / 8 at most 2 and so on, so the total work is bounded by n · (1/2 + 2/4 + 3/8 + ...) = 2n. Bottom-up construction uses at most about 2n comparisons.
On the array [9, 7, 8, 3, 5, 2, 6, 1] the “Build bottom-up” preset uses 11 comparisons and 7 swaps, while the “Build by insertion” preset needs 12 comparisons and 9 swaps. The gap grows on reverse-ordered input: for [8, 7, 6, 5, 4, 3, 2, 1] insertion needs 13 comparisons and 13 swaps against bottom-up's 10 comparisons.
1static void buildHeap(int[] a, int n) {2for (int i = n / 2 - 1; i >= 0; i--) siftDown(a, n, i); // the last n / 2 entries are leaves already3}4static void siftDown(int[] a, int n, int i) {5while (true) {6int l = 2 * i + 1, r = l + 1, m = i;7if (l < n && a[l] < a[m]) m = l;8if (r < n && a[r] < a[m]) m = r;9if (m == i) return;10swap(a, i, m);11i = m;12}13}
Best
O(n)
Average
O(n)
Worst
O(n)
Space
O(1)
Binary heap: sift-up, sift-down, insert, extract, replace, decrease-key and build-heap
Interactive · edit the inputmin-heap as a tree · 8 nodes
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
heap array (index = position in the tree)
Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root.
1static void siftUp(int[] a, int i) {2while (i > 0) {3int p = (i - 1) / 2; // parent4if (a[p] <= a[i]) break; // heap order restored5swap(a, p, i);6i = p;7}8}9static void siftDown(int[] a, int n, int i) {10while (true) {11int l = 2 * i + 1, r = l + 1, m = i;12if (l < n && a[l] < a[m]) m = l; // find the smallest of the node and its children13if (r < n && a[r] < a[m]) m = r;14if (m == i) break; // both children are larger: done15swap(a, i, m);16i = m;17}18}19static int size;20static void insert(int[] a, int x) { a[size] = x; siftUp(a, size++); }21static int extractMin(int[] a) {22int min = a[0];23a[0] = a[--size]; // move the last leaf to the root24siftDown(a, size, 0);25return min;26}27static void buildHeap(int[] a, int n) {28for (int i = n / 2 - 1; i >= 0; i--) siftDown(a, n, i); // the last n/2 entries are leaves already29}30static int replaceRoot(int[] a, int x) { // pop and push in one sift31int min = a[0];32a[0] = x;33siftDown(a, size, 0);34return min;35}36static void decreaseKey(int[] a, int i, int x) { a[i] = x; siftUp(a, i); }
Live state
size
8
Execution trace (dry run table)Show 24 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L1 | size=8 | Heapify 8 values in place: the last 4 entries are leaves, so start at the last internal node and sift each one down, moving toward the root. |
| 2 | L28 | size=8 index=3 | Sift down the node at index 3 (3). Everything below it is already a heap. |
| 3 | L12 | size=8 node=3 | Compare 3 with its only child 1. The smaller child is 1. |
| 4 | L15 | size=8 node=3 | Swap 3 with 1: the smaller value moves up. |
| 5 | L12 | size=8 node=3 | Compare 3 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 6 | L28 | size=8 index=2 | Sift down the node at index 2 (8). Everything below it is already a heap. |
| 7 | L12 | size=8 node=8 | Compare 8 with its children 2 and 6. The smaller child is 2. |
| 8 | L15 | size=8 node=8 | Swap 8 with 2: the smaller value moves up. |
| 9 | L12 | size=8 node=8 | Compare 8 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 10 | L28 | size=8 index=1 | Sift down the node at index 1 (7). Everything below it is already a heap. |
| 11 | L12 | size=8 node=7 | Compare 7 with its children 1 and 5. The smaller child is 1. |
| 12 | L15 | size=8 node=7 | Swap 7 with 1: the smaller value moves up. |
| 13 | L12 | size=8 node=7 | Compare 7 with its only child 3. The smaller child is 3. |
| 14 | L15 | size=8 node=7 | Swap 7 with 3: the smaller value moves up. |
| 15 | L12 | size=8 node=7 | Compare 7 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 16 | L28 | size=8 index=0 | Sift down the node at index 0 (9). Everything below it is already a heap. |
| 17 | L12 | size=8 node=9 | Compare 9 with its children 1 and 2. The smaller child is 1. |
| 18 | L15 | size=8 node=9 | Swap 9 with 1: the smaller value moves up. |
| 19 | L12 | size=8 node=9 | Compare 9 with its children 3 and 5. The smaller child is 3. |
| 20 | L15 | size=8 node=9 | Swap 9 with 3: the smaller value moves up. |
| 21 | L12 | size=8 node=9 | Compare 9 with its only child 7. The smaller child is 7. |
| 22 | L15 | size=8 node=9 | Swap 9 with 7: the smaller value moves up. |
| 23 | L12 | size=8 node=9 | Compare 9 with its children: none, it is a leaf. Neither is smaller: heap order holds here. |
| 24 | L1 | size=8 heap order=holds | [1, 3, 2, 7, 5, 8, 6, 9]: heap order holds. 11 comparisons and 7 swaps. |
Practice ladder
Now try it yourself
Easy
- Last Stone Weight
- Relative Ranks
Medium
- Kth Largest Element in an Array
- Sort an Array (heap sort)
Advanced
- Minimum Number of Refueling Stops
- Maximum Performance of a Team
Key takeaways
- Sift-up moves a node toward the root, sift-down toward the leaves along the smaller child; both cost O(log n).
- Insert adds at the first free slot and sifts up; extract moves the last leaf to the root and sifts down.
- Replace-root does one sift where poll plus add would do two.
- Bottom-up build-heap costs O(n), at most about 2n comparisons, against O(n log n) for repeated insertion.