Introduction

A binary heap is a complete binary tree, stored in an array, in which every parent is at least as large as its children (a max-heap) or at most as large (a min-heap). It answers one question fast: "which element has the highest priority?" The answer sits at the root, and the structure can be repaired in O(lg⁡n)O(\lg n) time after an insertion or a removal.

That makes the heap the standard implementation of a priority queue, used in heapsort, in Dijkstra's shortest-path algorithm and Prim's minimum spanning tree algorithm, in Huffman coding, and in priority scheduling of processes.

A heap is not a search structure: it does not keep keys sorted and cannot find an arbitrary key quickly. It keeps only enough order to expose the maximum (or minimum).

Prerequisites

  • Trees and Binary Trees: complete binary trees, height convention (edges), array representation.
  • Arrays: index arithmetic.
  • Logarithms and geometric sums; the sum ∑h/2h\sum h/2^h is derived below.

Core concepts

Shape property and heap property

A binary heap must satisfy two conditions.

  1. Shape property. The tree is a complete binary tree: all levels are full except possibly the last, which is filled from the left.
  2. Heap property. For a max-heap, A[parent(i)]≥A[i]A[\text{parent}(i)] \ge A[i] for every node ii other than the root. For a min-heap, the inequality is reversed.

The heap property is local (parent versus child), but by transitivity it implies that every node is at least as large as everything in its subtree. Hence the maximum of a max-heap is at the root. Nothing is said about the order between siblings or between different subtrees.

Min versus max. Every statement in this note has a mirror image: swap "max" with "min", "largest" with "smallest" and ≥\ge with ≤\le. The examples use max-heaps.

Array layout (1-indexed)

Store the nodes level by level in A[1..n]A[1..n]. Then, for 1≤i≤n1 \le i \le n:

PARENT(i)=⌊i/2⌋,LEFT(i)=2i,RIGHT(i)=2i+1,\text{PARENT}(i) = \lfloor i/2 \rfloor, \qquad \text{LEFT}(i) = 2i, \qquad \text{RIGHT}(i) = 2i + 1,

where a child index larger than nn means the child does not exist. With 0-based arrays the formulas are ⌊(i−1)/2⌋\lfloor (i-1)/2 \rfloor, 2i+12i+1 and 2i+22i+2. These notes use 1-based indexing for heaps; check the convention in every question.

Useful consequences for a heap of nn elements:

  • Height =⌊lg⁡n⌋= \lfloor \lg n \rfloor for n≥1n\ge1 (empty height −1-1); (edge convention).
  • Leaves are exactly A[⌊n/2⌋+1..n]A[\lfloor n/2 \rfloor + 1 .. n], so there are ⌈n/2⌉\lceil n/2 \rceil leaves; internal nodes are A[1..⌊n/2⌋]A[1 .. \lfloor n/2 \rfloor].
  • In a max-heap the minimum is one of the leaves; finding it still takes Θ(n)\Theta(n) time.
  • The second largest element is at index 2 or 3. More generally, the kk-th largest element is at depth at most k−1k - 1. For n=7n = 7, over all 7!7! inputs the 2nd largest appears only at indices 2 and 3, and the 3rd largest anywhere in indices 2 to 7, never at the root. This bounds the search region for the kk largest elements.
A max-heap as a tree and as an array
Figure 1. The max-heap 25 13 20 8 7 17 2 5 4 drawn as a tree, with each node's array index, and the same data as an array.

The core operations

Pseudocode uses 1-based arrays, <- for assignment and A.size for the number of elements currently in the heap.

MAX-HEAPIFY (sift down)

Precondition: the subtrees rooted at LEFT(i)\text{LEFT}(i) and RIGHT(i)\text{RIGHT}(i) are max-heaps, but A[i]A[i] may be smaller than a child. MAX-HEAPIFY moves A[i]A[i] down until the subtree rooted at ii is a max-heap.

Text
MAX-HEAPIFY(A, i)                    // iterative; O(1) auxiliary space
    while i <= A.size
        l <- 2i
        r <- 2i + 1
        largest <- i
        if l <= A.size and A[l] > A[largest] then largest <- l
        if r <= A.size and A[r] > A[largest] then largest <- r
        if largest = i then return
        exchange A[i] with A[largest]
        i <- largest

Each level costs at most two comparisons and one exchange, so the time is O(hi)O(h_i), where hih_i is the height of node ii; from the root that is O(lg⁡n)O(\lg n).

BUILD-MAX-HEAP (bottom-up)

Text
BUILD-MAX-HEAP(A, n)
    A.size <- n
    for i <- floor(n/2) downto 1
        MAX-HEAPIFY(A, i)

Leaves are already one-element heaps, so the loop starts at the last internal node ⌊n/2⌋\lfloor n/2 \rfloor. Going downwards in index guarantees that when node ii is processed, both of its subtrees are already heaps, which is the precondition of MAX-HEAPIFY.

Worked example: building a heap

Input A=[5,13,2,25,7,17,20,8,4]A = [5, 13, 2, 25, 7, 17, 20, 8, 4], n=9n = 9, so the loop runs i=4,3,2,1i = 4, 3, 2, 1.

StepNode ii (value)What happensArray after the stepExchanges
i=4i = 425Children 8 and 4 are smaller; no change5 13 2 25 7 17 20 8 40
i=3i = 32Larger child is 20 (index 7); exchange5 13 20 25 7 17 2 8 41
i=2i = 213Larger child is 25 (index 4); exchange; at index 4, 13 beats 8 and 45 25 20 13 7 17 2 8 41
i=1i = 15Exchange with 25 (index 2), then with 13 (index 4), then with 8 (index 8)25 13 20 8 7 17 2 5 43

Result: the max-heap 25,13,20,8,7,17,2,5,425, 13, 20, 8, 7, 17, 2, 5, 4 after 5 exchanges.

Bottom-up build-heap trace
Figure 2. The input array and the tree after each of the four MAX-HEAPIFY calls (i=4,3,2,1i = 4, 3, 2, 1), with the moving key and its path highlighted.

Why BUILD-MAX-HEAP is Θ(n)\Theta(n), not Θ(nlg⁡n)\Theta(n \lg n)

The easy bound multiplies about n/2n/2 calls by O(lg⁡n)O(\lg n) per call and gets O(nlg⁡n)O(n \lg n). That bound is correct but loose, because most nodes are near the bottom and sift down only a short distance.

A node of height hh costs O(h)O(h). In an nn-element heap there are at most ⌈n/2h+1⌉\lceil n/2^{h+1} \rceil nodes of height hh, and for 0≤h≤⌊lg⁡n⌋0 \le h \le \lfloor \lg n \rfloor this is at most n/2hn/2^h (because ⌈x⌉≤2x\lceil x \rceil \le 2x whenever x≥1/2x \ge 1/2). So the total work is at most

∑h=0⌊lg⁡n⌋n2h c h  ≤  c n∑h=0∞h2h  =  2cn,\sum_{h=0}^{\lfloor \lg n \rfloor} \frac{n}{2^h}\, c\,h \;\le\; c\,n \sum_{h=0}^{\infty} \frac{h}{2^h} \;=\; 2cn,

using ∑h≥0hxh=x/(1−x)2\sum_{h \ge 0} h x^h = x/(1-x)^2 at x=1/2x = 1/2. The loop also touches ⌊n/2⌋\lfloor n/2 \rfloor nodes, so the time is Ω(n)\Omega(n) as well. Hence BUILD-MAX-HEAP runs in Θ(n)\Theta(n).

An exact count makes this concrete. Each exchange moves a key down one level, so the number of exchanges is at most the sum of the heights of all nodes. For a perfect tree of height hh (so n=2h+1−1n = 2^{h+1} - 1) that sum is n−(h+1)=n−lg⁡(n+1)n - (h + 1) = n - \lg(n+1): 4 for n=7n = 7, 11 for n=15n = 15, 57 for n=63n = 63. Over all 7!7! inputs, the maximum number of exchanges for n=7n = 7 is exactly 4 (reached, for example, by the increasing input 1 2 3 4 5 6 7).

Number of nodes at each height and the work they cost
Figure 3. A perfect heap of 15 nodes with each node labelled by its height: 8 nodes cost 0, 4 cost 1, 2 cost 2 and 1 costs 3, for a total of 11.

Top-down construction is different. Building a heap by nn successive INSERT calls costs O(nlg⁡n)O(n \lg n) in the worst case (for example, keys arriving in increasing order into a max-heap, where each new key rises to the root). Only the bottom-up method is Θ(n)\Theta(n).

INSERT and INCREASE-KEY (sift up)

Text
MAX-HEAP-INSERT(A, key)
    A.size <- A.size + 1
    A[A.size] <- key
    i <- A.size
    while i > 1 and A[floor(i/2)] < A[i]
        exchange A[i] with A[floor(i/2)]
        i <- floor(i/2)

INCREASE-KEY at index ii sets the larger value and runs the same while loop from ii. DECREASE-KEY in a max-heap runs MAX-HEAPIFY from ii instead. Each is O(lg⁡n)O(\lg n).

EXTRACT-MAX

Text
HEAP-EXTRACT-MAX(A)
    if A.size < 1 then error "heap underflow"
    max <- A[1]
    A[1] <- A[A.size]
    A.size <- A.size - 1
    MAX-HEAPIFY(A, 1)
    return max

Worked example: insert and extract

Start from the heap 25,13,20,8,7,17,2,5,425, 13, 20, 8, 7, 17, 2, 5, 4.

  • Insert 30. Place it at index 10. Its parent (index 5) holds 7: exchange. Parent (index 2) holds 13: exchange. Parent (index 1) holds 25: exchange. Result 30,25,20,8,13,17,2,5,4,730, 25, 20, 8, 13, 17, 2, 5, 4, 7, after 3 exchanges.
  • Extract-max (from the original 9-element heap). Return 25; move the last element 4 to the root; sift down: exchange with 20 (index 3), then with 17 (index 6). Result 20,13,17,8,7,4,2,520, 13, 17, 8, 7, 4, 2, 5, after 2 exchanges.
  • Increase-key (from the original heap): change A[9]A[9] from 4 to 22. It rises past 8 (index 4) and 13 (index 2) and stops below 25. Result 25,22,20,13,7,17,2,5,825, 22, 20, 13, 7, 17, 2, 5, 8.

Both panels of Figure 4 start from the same original heap 25,13,20,8,7,17,2,5,425, 13, 20, 8, 7, 17, 2, 5, 4: the extract-max panel does not contain the inserted 30, so it returns 25, not 30.

Insert 30 and extract-max on the example heap
Figure 4. Left: the path of 30 rising from index 10 to the root. Right: the path of 4 sinking from the root after the maximum is removed.

Cost of operations

Under unit-cost key comparisons/array accesses, bounds below are worst-case unless marked otherwise. Best cases for insert, extract-max, known-index key update/delete and arbitrary-key search are constant (e.g. no sift needed, delete the last element, or find the root). Find-max is always constant; bottom-up build is linear; min-scan is linear; concatenate-and-build merge is linear for every input. Average-case bounds require a distribution and are deferred. For a binary max-heap with nn elements:

OperationTimeNote
Find maximumΘ(1)\Theta(1)A[1]A[1]
InsertO(lg⁡n)O(\lg n)Sift up; Θ(1)\Theta(1) if the new key is small
Extract-maxO(lg⁡n)O(\lg n)Sift down from the root
Increase-key or decrease-key at a known indexO(lg⁡n)O(\lg n)Sift up or down
Delete at a known indexO(lg⁡n)O(\lg n)Replace by the last element, then sift up or down
Build from nn keys (bottom-up)Θ(n)\Theta(n)Analysis above
Search for an arbitrary keyO(n)O(n)No ordering to exploit
Find minimum in a max-heapΘ(n)\Theta(n)Scan the ⌈n/2⌉\lceil n/2 \rceil leaves
Merge two heaps of sizes mm and nnΘ(m+n)\Theta(m + n)Concatenate arrays and build

Space is Θ(n)\Theta(n) for the array. BUILD-MAX-HEAP, HEAP-EXTRACT-MAX and HEAPSORT all call the displayed iterative MAX-HEAPIFY, so those implementations use O(1)O(1) auxiliary workspace. A recursive heapify would instead need O(lg⁡n)O(\lg n) worst-case call-stack space.

Heapsort in brief

Heapsort builds a max-heap, then repeatedly exchanges the root with the last element of the heap, shrinks the heap by one, and sifts the new root down.

Text
HEAPSORT(A, n)
    BUILD-MAX-HEAP(A, n)
    for i <- n downto 2
        exchange A[1] with A[i]
        A.size <- A.size - 1
        MAX-HEAPIFY(A, 1)
  • Time O(nlg⁡n)O(n \lg n) in the worst case: Θ(n)\Theta(n) to build plus n−1n - 1 sift-downs of O(lg⁡n)O(\lg n) each. Distinct keys have worst-case Θ(nlg⁡n)\Theta(n\lg n); with all equal keys, sift-downs terminate immediately and total time is Θ(n)\Theta(n). Average-input analysis is deferred rather than assumed. It is not adaptive: sorted input does not make it faster in general.
  • In place: O(1)O(1) extra space, using the iterative MAX-HEAPIFY displayed here.
  • Not stable. Sorting the records 2a,2b,12_a, 2_b, 1 (compared on the number only) produces 1,2b,2a1, 2_b, 2_a: the two equal keys swap their original order.
  • Full treatment: Heapsort.

Finding the $k$-th smallest element

  • With a min-heap of all nn elements: build in Θ(n)\Theta(n), then extract-min kk times: O(n+klg⁡n)O(n + k \lg n). For the example keys 5,13,2,25,7,17,20,8,45, 13, 2, 25, 7, 17, 20, 8, 4, the third extraction returns 5, the 3rd smallest.
  • Without destroying the heap: keep a second, small min-heap of "candidates", starting with the root. Each time the smallest candidate is removed, add its two children from the big heap. After kk removals the kk-th one removed is the answer, in O(klg⁡k)O(k \lg k) time, because the candidate heap never holds more than k+1k + 1 items.

d-ary heaps

For integer d≥2d\ge2, a dd-ary heap lets each node have up to dd children, still complete and still stored level by level. With 1-based indexing, the children of node ii are at indices

d(i−1)+2,  …,  d i+1,andPARENT(j)=⌊j−2d⌋+1.d(i-1) + 2, \;\ldots,\; d\,i + 1, \qquad \text{and} \qquad \text{PARENT}(j) = \left\lfloor \frac{j - 2}{d} \right\rfloor + 1 .

For d=2d = 2 these reduce to 2i,2i+12i, 2i+1 and ⌊j/2⌋\lfloor j/2 \rfloor. Example for d=3d = 3: the children of node 2 are at 5, 6 and 7, and the parent of node 10 is node 3.

The height is Θ(log⁡dn)\Theta(\log_d n); for n=100n = 100 it is 4 when d=3d = 3 and 6 when d=2d = 2. Insert and increase-key cost O(log⁡dn)O(\log_d n) (fewer levels to climb), while extract-max costs O(dlog⁡dn)O(d \log_d n) because each level compares up to dd children. A larger dd therefore helps algorithms with many key increases and few extractions.

Important properties and observations

  • A sorted array (descending for a max-heap) is always a valid heap; a heap is usually not sorted.
  • Every subtree of a heap is a heap. Every path from the root to a leaf is sorted (non-increasing in a max-heap).
  • The number of distinct max-heaps that can be formed from nn distinct keys has H(0)=H(1)=1H(0)=H(1)=1 and, for n≥2n\ge2, satisfies H(n)=(n−1L)H(L) H(n−1−L)H(n) = \binom{n-1}{L} H(L)\, H(n-1-L), where LL is the size of the left subtree forced by the complete shape. Values: H(1..7)=1,1,2,3,8,20,80H(1..7) = 1, 1, 2, 3, 8, 20, 80.
  • Bottom-up build is Θ(n)\Theta(n); nn insertions are O(nlg⁡n)O(n \lg n). These are different algorithms and can give different final arrays from the same input: for input 1, 2, 3, bottom-up gives 3 2 1 and three insertions give 3 1 2.
  • A heap gives Θ(1)\Theta(1) access only to one end. For both the minimum and the maximum, use two heaps or a different structure.

GATE examination perspective

  • Array traces. Questions give an array and ask for the result of build-heap, an insertion or a deletion. Work level by level, and record the array after each operation. Check the heap property at the end.
  • "Is this array a heap?" Check A[⌊i/2⌋]≥A[i]A[\lfloor i/2 \rfloor] \ge A[i] for every i≥2i \ge 2; the shape is automatic for an array without gaps.
  • Counting comparisons or exchanges. State what is counted. MAX-HEAPIFY as written above makes up to two key comparisons per level (left child with the current node, then right child with the larger of the two).
  • Complexity traps. Build-heap is Θ(n)\Theta(n), not Θ(nlg⁡n)\Theta(n \lg n). Searching a heap is O(n)O(n). Finding the minimum of a max-heap is Θ(n)\Theta(n).
  • Indexing traps. 0-based and 1-based formulas differ, so an off-by-one index is a likely error.
  • Position of the kk-th largest. Depth at most k−1k - 1 (see "Array layout"); the smallest is a leaf.

Summary

Sources

  1. T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein, Introduction to Algorithms, 3rd ed., MIT Press, 2009: Chapter 6 (heaps, MAX-HEAPIFY, BUILD-MAX-HEAP and its linear-time analysis, heapsort, priority queues, and Problem 6-2 on d-ary heaps), Sections 6.1 to 6.5.
  2. P. Morin, Open Data Structures (Python edition), Section 10.1 "BinaryHeap: An Implicit Binary Tree", (consulted 2026-11-09; uses 0-based indexing and a min-heap).

Next steps