Exercises — Heapify — bottom-up O(n) build
This is the practice deck for bottom-up heapify. Work each problem before opening its solution. The problems climb five levels: L1 Recognition → L2 Application → L3 Analysis → L4 Synthesis → L5 Mastery. Everything you need was built in the parent note; if a symbol looks unfamiliar, jump back and re-read that section first.
Quick reference (0-based array ↔ complete tree):
L1 — Recognition
Goal: read the definitions off the array without doing any real work yet.
Recall Solution Q1
(a) . (b) Start index . So we sift nodes . (c) Everything from the start index to the end is a leaf: indices → that is leaves. Check: internal nodes indices ; leaves ; ✓.
Recall Solution Q2
Index has value .
- parent index → value .
- left child index → value .
- right child index → value . It has children (indices both ), so index is internal.
L2 — Application
Goal: actually run buildHeap and siftDown by hand, tracking every swap.
Recall Solution Q3
, last internal . Process .
Start: [2, 7, 4, 1, 9, 3].
i=2 (value ; left child index → value ; right child index , so none): → no swap. [2,7,4,1,9,3].
i=1 (value ; left child index → value , right child index → value ): largest is at index → swap. [2,9,4,1,7,3]. The demoted is now at index , a leaf → stop.
i=0 (value ; left child index → value , right child index → value ): largest is → swap idx . [9,2,4,1,7,3]. Follow the down: now at index , left child index → value , right child index → value → largest → swap idx . [9,7,4,1,2,3]. The is at index , a leaf → stop.
Result: [9, 7, 4, 1, 2, 3].
Verify heap property: ✓; ✓; ✓.
Recall Solution Q4
, last internal . Process . (Min-heap: parent children.)
Start: [8, 3, 6, 1, 5, 9, 2].
i=2 (value ; left child index → value , right child index → value ): smallest is → swap idx . [8,3,2,1,5,9,6]. now at index , a leaf → stop.
i=1 (value ; left child index → value , right child index → value ): smallest is → swap idx . [8,1,2,3,5,9,6]. now at index , a leaf → stop.
i=0 (value ; left child index → value , right child index → value ): smallest is → swap idx . [1,8,2,3,5,9,6]. Follow the : now index , left child index → value , right child index → value → smallest → swap idx . [1,3,2,8,5,9,6]. now at index , a leaf → stop.
Result: [1, 3, 2, 8, 5, 9, 6].
Verify: ✓; ✓; ✓.
L3 — Analysis
Goal: reason about counts, costs, and the two build strategies rather than crunch one array.
Recall Solution Q5
, . Recall height is measured from the bottom, so are the leaves and is the single root. Exact counts (a perfect tree doubles each level going down):
| height | 4 (root) | 3 | 2 | 1 | 0 (leaves) |
|---|---|---|---|---|---|
| nodes | 1 | 2 | 4 | 8 | 16 |
Sum ✓. Leaf bound at : ✓ — matches the exact leaf count. Notice the punchline visually: the two shortest heights ( and ) already hold of the nodes — the numerous nodes are the cheap ones.
Recall Solution Q6
Multiply each height's node-count by its cost : Loose bound: . So the true work is under (since ), while the loose bound overcounts by nearly . This is the gap between (truth) and (loose bound) in a single concrete tree.
Recall Solution Q7
The infinite sum (parent note) is . The remaining tail is only , and it keeps shrinking. This is why heapify is linear: written carefully (with no Big-O misplaced inside the inequality), The multiplier is a constant (it converges to ), never a growing .
L4 — Synthesis
Goal: combine heapify with the algorithms that depend on it.
Recall Solution Q8
Build max-heap. , last internal . Only .
i=0 (value ; left child index → value , right child index → value ): both → no swap. Heap: [3,1,2].
Extraction phase (swap root with last active element, shrink, sift root):
- Swap idx :
[2,1,3]; is now sorted-tail. Active size . Sift index (value ; left child index → value ): → stop.[2,1 | 3]. - Swap idx :
[1,2,3]; active size . Nothing to sift.[1 | 2,3]. - One element left → done.
Sorted result (ascending): [1, 2, 3] ✓.
Recall Solution Q9
Build. , last internal . Process .
- i=1 (value ; left child index → value ; right child index , none): → swap.
[6,4,8,2]. - i=0 (value ; left child index → value , right child index → value ): largest → swap idx .
[8,4,6,2]. at index , leaf → stop. Heap after build:[8, 4, 6, 2]. Check: ✓; ✓.
extractMax. Return . Move last element () to root, shrink to size : [2,4,6]. Sift index (value ; left child index → value , right child index → value ): largest → swap idx . [6,4,2]. leaf → stop.
Returned max ; heap now [6, 4, 2] ✓ ().
L5 — Mastery
Goal: derive and generalise, not just execute.
Recall Solution Q10
Start from the geometric series (valid for ): Why differentiate? We want a to appear in front of each term (because our per-node cost is ). Differentiation w.r.t. pulls the exponent down as a coefficient — exactly the tool that manufactures a factor of : Multiply by to restore the exponent from to : At : .
Now the heapify bound — and where the comes from. Recall from the parent note that a (near-)complete tree has at most nodes at height (leaves are , height-1 nodes , and so on — each level up halves the count). A node at height costs at most sift-steps. So the total is the sum over heights of (count)(cost): The factor is exactly the pulled out front from the node-count bound . Extending the finite sum to infinity only overestimates, so: The step that kills the : replacing the finite sum by the infinite and observing it converges to the constant . Because the series is bounded independent of (and hence of ), no factor survives.
Recall Solution Q11
Construction. Insert the values in increasing order into a max-heap. Each newly inserted value is the largest so far, so it must bubble from a leaf all the way to the root.
The -th insertion sits in a heap of size , whose height is , and it climbs the full height:
(The sum by Stirling.)
Why bottom-up wins. siftUp moves the numerous leaves toward the root — the long, expensive direction. Bottom-up siftDown instead assigns the long journeys only to the rare tall nodes near the root, and the many leaves do zero work. The direction of travel, relative to where the crowd lives, is the whole difference.
The figure below draws a small tree. The many leaves are the red nodes at the bottom row (indices 3, 4, 5, 6); the root is the single white node at the top (index 0). Two arrows are overlaid: the red curved arrow labelled siftUp starts among the many red leaves and travels the entire height up to the root — that is the expensive path taken by every inserted leaf. The black arrow labelled siftDown starts at the lone root and heads downward — only that single rare node makes a long trip. Reading the picture in words: "expensive path × many nodes" gives ; "long path × one node (plus cheaper trips for the few near the top)" gives .

Recall Solution Q12
Nodes at height in a perfect tree (height : node; height : leaves). We first need the finite identity . Here is its derivation (a self-contained telescoping trick). Let . Multiply by (this shifts every term one slot right): Subtract this from term-by-term (this is the same "" trick used to sum a geometric series, but here it leaves a clean geometric remainder): The remaining is an ordinary geometric series. So: (Sanity check : , exactly matching Q7 ✓.) Now finish. Substitute this identity back: Since , we get . Heapify does fewer than sift-steps — even the hidden constant is below . Spot check (): , matching Q6 exactly ✓.
Recall Master checklist (reveal after finishing all 12)
- Start index , sift down, march up; for it is a no-op. ✓
siftDownloops until leaf or property holds — never one swap. ✓- Node at height (measured from the bottom, leaves ) costs ; height- node count . ✓
- The magic constant: → build is , in fact steps. ✓
- Heapsort = build extractions total. ✓
siftUpbuild = ; direction toward the crowded leaves is the enemy. ✓