Heap sort
Heap sort as an algorithm: build a max-heap bottom-up in linear time, then repeatedly swap the maximum to the end and sift down. Why building costs at most 2n comparisons, why the extraction phase makes it Theta(n log n) in every case, its constants against quicksort and merge sort, and its properties (in place, not stable, not adaptive).
Why it exists
Merge sort guarantees but needs a second array; quicksort is in place and fast but can degrade to . Heap sort gets both guarantees at once: in place and in the worst case. It is the sort to name when an exam asks for "in place with guaranteed ". The price is a larger constant than quicksort and no stability.
Prerequisites
The structure and its operations (sift up, sift down, build) are in binary heaps and heap applications. This page adds the analysis as a sorting algorithm and the comparison with the other sorts.
Precise definitions
Max-heap. An array in which and whenever those positions exist; the maximum is .
Sift down at position : while a child is larger than , swap with the larger child and move down. It costs at most two comparisons per level.
Heap sort. (1) Build: for sift down position (the leaves are already heaps). (2) Extract: for : swap and (the maximum goes to its final place) and sift down position in the heap . The array ends in increasing order.
How it works
The two animations below are real runs of the C code (the same functions that are used on the heap pages): first the bottom-up build, then the whole sort. In the second, green cells are in their final places.
Building a heap in place: sift down from n / 2 to 1
Real runC source
Array a (the heap is positions 1 to n; each node's children are at 2i and 2i + 1)
State
Blue = the position this step is working on, amber = a position being compared, red = positions just swapped, green = finished or in its final place, a filled blue cell = a value just placed, faded = not part of the structure at this step.
Step 1
An arbitrary array of 10 keys, viewed as a complete binary tree. Positions 6 to 10 are leaves and are already heaps of size 1.
Heapsort: build a max-heap, then move the maximum to the end again and again
Real runC source
Array a (the heap is positions 1 to n; each node's children are at 2i and 2i + 1)
State
Blue = the position this step is working on, amber = a position being compared, red = positions just swapped, green = finished or in its final place, a filled blue cell = a value just placed, faded = not part of the structure at this step.
Step 1
An arbitrary array of 7 keys. First it is turned into a max-heap.
Cost of building. A node of height (the longest path down to a leaf) is sifted down at most levels, two comparisons per level. A heap of nodes has at most nodes of height , so the total number of comparisons is about
More precisely the sum of the heights of all nodes is , so building uses at most comparisons, less than : building is , not . Inserting the keys one by one (sift up) has a worst case of .
Cost of extracting. There are extractions. Each sifts the new root down at most levels, two comparisons per level: at most comparisons each, so at most in all. The extraction phase must be in the worst case: heap sort is a comparison sort, which needs comparisons (lower bound), and the build takes only . In practice the key moved to the root is a former leaf and sinks almost to the bottom.
Total. for the build plus for the extractions: in the best, average and worst case; space extra. (If all keys are equal and the sift down uses a strict comparison it stops at once and the sort is linear; the usual statement for exams is for all cases.)
Measured. The program counts the comparisons of keys in the build (bottom-up and by repeated insertion) and of the complete sort, on a random input for .
#include <stdio.h>
static long cmps;
static void swap(int *x, int *y) { int t = *x; *x = *y; *y = t; }
/* a[1..n]: the subtree at i is made a max-heap, given that both subtrees below it already are */
static void sift_down(int a[], int n, int i) {
for (;;) {
int l = 2 * i, r = l + 1, big = i;
if (l <= n) { cmps++; if (a[l] > a[big]) big = l; }
if (r <= n) { cmps++; if (a[r] > a[big]) big = r; }
if (big == i) return;
swap(&a[i], &a[big]);
i = big;
}
}
static void sift_up(int a[], int i) {
while (i > 1) {
cmps++;
if (a[i / 2] >= a[i]) return;
swap(&a[i / 2], &a[i]);
i /= 2;
}
}
static long by_build(int a[], int n) { /* bottom-up: sift down from n/2 to 1 */
cmps = 0;
for (int i = n / 2; i >= 1; i--) sift_down(a, n, i);
return cmps;
}
static long by_insert(int a[], int n) { /* n insertions, each sifted up */
cmps = 0;
for (int i = 2; i <= n; i++) sift_up(a, i);
return cmps;
}
static long sort_all(int a[], int n) { /* heap sort: build, then n - 1 extractions */
by_build(a, n);
for (int e = n; e > 1; e--) { swap(&a[1], &a[e]); sift_down(a, e - 1, 1); }
for (int i = 2; i <= n; i++) if (a[i - 1] > a[i]) return -1;
return cmps;
}
int main(void) {
static int a[1100], b[1100];
int ns[] = {15, 63, 255, 1023};
unsigned s = 4242;
printf("%5s %8s %8s %9s\n", "n", "build", "insert", "heapsort");
for (int q = 0; q < 4; q++) {
int n = ns[q];
for (int i = 1; i <= n; i++) { a[i] = i; }
for (int i = n; i > 1; i--) { s = s * 1103515245u + 12345u; int j = 1 + (int)((s >> 8) % (unsigned)i); swap(&a[i], &a[j]); }
for (int i = 1; i <= n; i++) b[i] = a[i];
long cb = by_build(a, n);
for (int i = 1; i <= n; i++) a[i] = b[i];
long ci = by_insert(a, n);
for (int i = 1; i <= n; i++) a[i] = b[i];
long cs = sort_all(a, n);
printf("%5d %8ld %8ld %9ld\n", n, cb, ci, cs);
}
return 0;
} n build insert heapsort
15 22 21 76
63 112 127 564
255 472 551 3281
1023 1890 2323 17292Comparisons of heap construction and heap sort, measured: start
Computed by a script, not typed| n | bottom-up build | per key | n insertions | per key | complete heap sort | per n log2 n |
|---|---|---|---|---|---|---|
| 15 | ||||||
| 63 | ||||||
| 255 | ||||||
| 1023 |
Step 0
One random input for each n = 2^k - 1. Bottom-up construction (sift down from n/2 to 1) is compared with building by n insertions, and with the whole heap sort. The program is on this page.
Bottom-up building stayed below comparisons per key. (In this single run repeated insertion was cheaper for the smallest input and dearer for the larger ones; its guaranteed worst case is , while bottom-up is guaranteed linear.) The whole sort used between and times comparisons, rising with and never above the bound ; for the larger that is more than the average of quicksort (quicksort) and than merge sort's (merge sort).
Properties.
- In place: only a few variables besides the array.
- Not stable: the root is swapped with the last leaf, a long jump that ignores equal keys (the measured test of stability and comparison of sorts shows of small arrays reordered).
- Not adaptive: already sorted input costs the same as random input.
- Cache behaviour: the parent and children are far apart in memory, so heap sort is slower in practice than merge sort or quicksort, which scan memory sequentially.
- A max-heap produces increasing order. Using a min-heap and writing the extracted keys to a new array (or to the end of a reversed array) gives decreasing order.
Heaps for partial answers. The largest of keys: build a max-heap and extract times, ; or keep a min-heap of the largest seen so far, . Both beat sorting when is small.
Worked examples
EX 1Build a max-heap
EasyArray (). Sift down positions . Position (key ) has the single child at position (key ): swap, giving . Position (key ) is larger than its children : no change. Position (key ): the larger child is at position : swap, ; the key is now at position with children : swap with , . Result: the heap after swaps. The number of swaps is at most the sum of heights, ( has two 1 bits).
EX 2One extraction
EasyFrom the heap : swap the root with the last key: ( is final). Sift down the root in the heap of size : the children are and , the larger is : swap, . The key is at position , whose children would be at positions and , outside the heap: stop. Two comparisons were made.
EX 3The bound on comparisons
MediumFor the build takes at most comparisons (the measured is below it) and the extraction phase at most more, so heap sort uses at most about ; the measured total on a random input was . Merge sort's worst case for keys is about .
EX 4Which sort does the job?
MediumSort keys on a device with memory for only a few extra values and a requirement that the worst case be : heap sort (merge sort needs a second array; quicksort may reach ). The same but equal keys must keep their order: none of the three comparison sorts qualifies without extra memory; attach the original index as a tiebreaker (adds memory) or accept merge sort's array.
EX 5True or false about heap sort
GATE level(a) The time to build a heap from an arbitrary array of keys is : false, . (b) Heap sort is in the worst case: true. (c) Heap sort takes time on sorted input: false, (it is not adaptive). (d) Heap sort is stable: false. (e) The smallest key of a max-heap of distinct keys is in one of the leaves: true, the leaves are positions . (f) Finding the largest key of a max-heap: .
Cheat box
Cheat box
- Build: sift down from to ; , at most comparisons.
- Extract: times swap root with the last key, shrink, sift down: at most comparisons each.
- Total in best, average and worst case; at most about comparisons.
- In place (), not stable, not adaptive; poor cache behaviour.
- Max-heap gives increasing order.
- largest keys: or .
Traps and GATE patterns
Connections
Builds on
The structure is in binary heaps and its uses in heap applications, where the same two animations appear. The comparison with the other sorts is in stability and comparison of sorts; the competing methods are merge sort and quicksort. Priority queues built on heaps drive Dijkstra's algorithm and Huffman coding.
PYQs
3 previous year questions from your book. I re-solved each one myself (2 by running or simulating, 1 by reasoning only); the book's key is shown next to my answer. All PYQs
Consider a complete binary tree where the left and the right subtrees of the root are max heaps. The lower bound for the number of operations to convert the tree to a heap is
- (a)Ω(logn)
- (b)Ω(n)
- (c)Ω(nlogn)
- (d)Ω(n2)
Match the algorithms with their time complexities: Algorithm Time complexity P Towers of Hanoi with n disks i Θ(n2 ) Q Binary search given n sorted numbers ii Θ(nlog n) R Heap sort given n numbers at the worst case iii Θ(2n ) S Addition of two n × n matrices iv Θ(logn) ALGORITHMS Check Out Our Gate Courses & Secure Your Dream Rank !
- (a)P-(iii), Q-(iv), R-(i), S-(ii)
- (b)P-(iv), Q-(iii), R-(i), S-(ii)
- (c)P-(iii), Q-(iv), R-(ii), S-(i)
- (d)P-(iv), Q-(iii), R-(ii), S-(i)
The number of elements that can be sorted in Θ(log n) time using heap sort is
- (a)θ(1)
- (b)θ(√log n)
- (c)θ(log n / (loglog n))
- (d)θ(log n)
Self-check
Question 1
What is the time to build a max-heap from keys in an array, and why not ?
Show answerHide
: most nodes are near the bottom and sift down only a few levels; the sum of the heights is less than .
Question 2
What is the best-case time of heap sort on a sorted array of distinct keys?
Show answerHide
: heap sort is not adaptive.
Question 3
Name two properties of heap sort that make it preferable to quicksort and merge sort in some settings.
Show answerHide
It is in place (unlike merge sort on arrays) and has a worst case (unlike quicksort).
Question 4
Is heap sort stable?
Show answerHide
No.
Revision card
One-screen revision
- Build ( comparisons) + extractions of comparisons: always.
- In place, not stable, not adaptive; max-heap increasing.
- comparisons worst case, more than quicksort's average.
- largest: .
References
- ·Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (3rd edition), Chapter 6 Heapsort (6.3 building a heap, 6.4 the heapsort algorithm)
- ·Sedgewick and Wayne, Algorithms (4th edition), Section 2.4 Priority Queues (heapsort)
Verification
The heap structure, the two animations and the data-structure side of heapsort are on the page of heap applications (draft, machine-checked). Here the cost was re-measured: heap construction and heap sort were re-implemented in Python and run on EVERY permutation of up to 8 keys (bottom-up construction never needed more than 2(n - popcount(n)) comparisons, and the whole sort never more than 2 n floor(lg n)) and on 200 random inputs of up to 400 keys; all-equal keys with the strict comparison need a linear number of comparisons. The C program on this page produced the table of measured comparisons. The textbook section numbers are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (loop counts, recurrences, merge and quicksort counts and averages, hash probes and load-factor formulas, DP tables, MST and shortest-path runs, Floyd matrices); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; No other defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)