Build Heap from Array Heapify in DSA Rust - Time & Space Complexity
We want to understand how long it takes to build a heap from an array using heapify. This helps us see how the work grows as the array gets bigger.
What question are we trying to answer? How does the time needed change when the input size increases?
Analyze the time complexity of the following code snippet.
fn heapify(arr: &mut [i32], n: usize, i: usize) {
let mut largest = i;
let left = 2 * i + 1;
let right = 2 * i + 2;
if left < n && arr[left] > arr[largest] {
largest = left;
}
if right < n && arr[right] > arr[largest] {
largest = right;
}
if largest != i {
arr.swap(i, largest);
heapify(arr, n, largest);
}
}
fn build_heap(arr: &mut [i32]) {
let n = arr.len();
for i in (0..n / 2).rev() {
heapify(arr, n, i);
}
}
This code builds a max heap from an unsorted array by calling heapify from the bottom up.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The heapify function, which may call itself recursively to fix the heap property.
- How many times: The build_heap function calls heapify for each non-leaf node, about n/2 times.
Heapify runs faster near the bottom of the tree and slower near the top. So, the total work is less than doing full heapify n/2 times.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 30 operations |
| 100 | About 500 operations |
| 1000 | About 8000 operations |
Pattern observation: The total operations grow a bit faster than n but much slower than n squared.
Time Complexity: O(n)
This means building a heap from an array takes time proportional to the size of the array, growing linearly as the array grows.
[X] Wrong: "Heapify runs in O(n log n) because it calls heapify on n/2 nodes and each heapify takes O(log n)."
[OK] Correct: Not all heapify calls take log n time. Many are near the bottom and run faster. The total work adds up to O(n), not O(n log n).
Knowing that building a heap is O(n) helps you explain why heap sort is efficient. It shows you understand how recursive work adds up in tree structures.
"What if we changed the heapify to a non-recursive version? How would the time complexity change?"
Practice
heapify process when building a heap from an array?Solution
Step 1: Understand heap property
The heap property requires each parent node to be greater (max-heap) or smaller (min-heap) than its children.Step 2: Role of heapify
Heapify rearranges elements so this property holds for every subtree, starting from the bottom.Final Answer:
To rearrange the array elements to satisfy the heap property -> Option DQuick Check:
Heapify = Rearrange for heap property [OK]
- Thinking heapify sorts the array
- Confusing heapify with finding max/min element
- Assuming heapify reverses the array
heapify function on an array slice arr starting at index i with heap size n?Solution
Step 1: Understand Rust borrowing rules
To modify an array in Rust, pass a mutable reference:&mut arr.Step 2: Check parameter types
Indicesiandnare usually copied values, so pass by value.Final Answer:
heapify(&mut arr, i, n); -> Option BQuick Check:
Mutable reference for array + values for indices [OK]
- Passing array without &mut causes compile error
- Passing immutable reference &arr when mutation needed
- Passing references for integers unnecessarily
build_heap(&mut arr)? Assume build_heap calls heapify correctly to build a max-heap.
fn main() {
let mut arr = [3, 9, 2, 1, 4, 5];
build_heap(&mut arr);
println!("{:?}", arr);
}Solution
Step 1: Understand build_heap process
Build_heap calls heapify from last non-leaf node upwards to root, fixing subtree heaps.Step 2: Apply heapify on array [3,9,2,1,4,5]
Heapify rearranges to max-heap: root is largest, children smaller. Result is [9,4,5,1,3,2].Final Answer:
[9, 4, 5, 1, 3, 2] -> Option AQuick Check:
Max-heap root 9, children smaller [OK]
- Assuming array is sorted after build_heap
- Confusing max-heap with min-heap output
- Not applying heapify from last non-leaf node
fn heapify(arr: &mut [i32], i: usize, n: usize) {
let mut largest = i;
let l = 2 * i + 1;
let r = 2 * i + 2;
if l < n && arr[l] > arr[largest] {
largest = l;
}
if r < n && arr[r] > arr[largest] {
largest = r;
}
if largest != i {
arr.swap(i, largest);
heapify(arr, n, largest);
}
}
What is the bug in this code that prevents building a max-heap correctly?Solution
Step 1: Examine function signature
The signature isfn heapify(arr: &mut [i32], i: usize, n: usize).Step 2: Check recursive call
The recursive call isheapify(arr, n, largest);, which passesnasiandlargestasn: wrong parameter order.Final Answer:
The recursive call toheapifyuses wrong parameter order -> Option AQuick Check:
Recursive call must match signature: arr, i, n [OK]
- Swapping n and i in recursive call
- Miscalculating child indices
- Ignoring bounds checks for indices
[7, 2, 6, 3, 9, 1] using the standard heapify approach. Which modification should you make to the heapify function to correctly build a min-heap instead of a max-heap?Solution
Step 1: Understand heap property difference
Max-heap requires parent > children; min-heap requires parent < children.Step 2: Modify comparison operators
To build min-heap, change comparisons from '>' to '<' so smaller values rise to the top.Final Answer:
Change all '>' comparisons to '<' when comparing child and parent values -> Option CQuick Check:
Min-heap uses less-than comparisons [OK]
- Swapping child indices does not affect heap type
- Reversing array does not build min-heap
- Starting heapify from root breaks heap building
Ready to prove it?
Pass the test and this pattern moves unknown
