Skip to content
Part IA Lent Term

Building a Heap in Linear Time

Naive Build: Θ(nlogn)\Theta(n \log n)

Inserting nn elements one by one, each costing O(logn)O(\log n), gives Θ(nlogn)\Theta(n \log n) total. This is the obvious approach but not optimal.

Linear-Time Build: Bottom-Up Heapify

A more efficient method calls Max-Heapify on all non-leaf nodes, working bottom-up from n/2\lfloor n/2 \rfloor down to 1:

Build-Max-Heap(A):
  A.heap_size = A.length
  for i = floor(A.length / 2) downto 1:
    Max-Heapify(A, i)

The key insight: leaves (indices n/2+1\lfloor n/2 \rfloor + 1 to nn) are already 1-element max-heaps, so they need no work. Starting from the last non-leaf node and working upward ensures that when Max-Heapify is called on node ii, both subtrees of ii are already valid heaps.

Correctness

Loop invariant: At the start of each iteration, every node index >i> i is the root of a valid max-heap.

  • Initialisation (i=n/2i = \lfloor n/2 \rfloor): All nodes >n/2> \lfloor n/2 \rfloor are leaves, trivially heaps.
  • Maintenance: Max-Heapify fixes node ii; after the call, ii is also a heap root. Moving to i1i-1 preserves the invariant.
  • Termination (i=0i = 0): All nodes, including the root (index 1), are valid heaps.

Analysis: Why O(n)O(n)

The cost of Max-Heapify at node ii is proportional to the height of node ii, i.e. the number of levels below it. At level hh from the bottom (height hh), there are at most n/2h+1\lceil n / 2^{h+1} \rceil nodes, and each Max-Heapify costs O(h)O(h). Summing over all levels:

T(n)h=0lognn2h+1chcn2h=0h2h1T(n) \le \sum_{h=0}^{\lfloor \log n \rfloor} \left\lceil \frac{n}{2^{h+1}} \right\rceil \cdot c h \le c \cdot \frac{n}{2} \sum_{h=0}^{\infty} \frac{h}{2^{h-1}}

The infinite sum converges: h=0h/2h=2\sum_{h=0}^{\infty} h/2^{h} = 2. Therefore:

T(n)=O(n)T(n) = O(n)

Concrete Calculation

For n=15n = 15 (a perfect heap), the tree has 4 levels:

Level (from leaf)Height hhNodes at this levelWork per nodeTotal work
0 (leaves)0800
114cc4c4c
2222c2c4c4c
3 (root)313c3c3c3c

Total: 11c11c, whilst nlognn \log n would be 15×3.9c59c15 \times 3.9c \approx 59c. The linear-time build is substantially faster.

Step-by-Step Example

Input: A=[4,1,3,2,16,9,10,14,8,7]A = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7] (n=10n=10)

First non-leaf: 10/2=5\lfloor 10/2 \rfloor = 5, value 1616.

Initial array as tree:          After Build-Heap (final):
          4                                 16
      /       \                          /      \
     1         3                       14        10
   /   \     /   \                   /   \     /    \
  2    16   9     10                8     7   9      3
 / \   /                          / \   /
14  8 7                          2   4  1
StepiiActionKey effect
15Max-Heapify on node with value 16Already a heap (16 > 7)
24Max-Heapify on node with value 2Swap 2 with 14; [14,8][14, 8] heap
33Max-Heapify on node with value 3Swap 3 with 10; [10,9][10, 9] heap
42Max-Heapify on node with value 1Swap 1 with 16, then 1 with 7; heap
51Max-Heapify on node with value 4Swap 4 with 16, then 4 with 14, then 4 with 8

Final heap: [16,14,10,8,7,9,3,2,4,1][16, 14, 10, 8, 7, 9, 3, 2, 4, 1].

Summary

AspectDetail
Naive buildInsert nn elements: Θ(nlogn)\Theta(n \log n)
Efficient buildBottom-up heapify: Θ(n)\Theta(n)
Starting indexn/2\lfloor n/2 \rfloor (last non-leaf)
Why O(n)O(n)Most nodes (leaves) cost O(0)O(0); higher nodes are few
Series sumh/2h\sum h / 2^{h} converges to 2
Key insightWork is proportional to height, not depth