Build a Heap (Heapify)

So you have a plain array of numbers in any random order. You want to turn it into a heap. A heap is a tree-shaped arrangement where every parent is bigger than its children, stored inside an array. The question is how do you do that fast?

You could pick numbers one by one and insert them into a fresh heap. That works, but it is slow. There is a smarter way called heapify. It fixes the whole array in one bottom-up pass. Let us see how.

🎯 What β€œBuild a Heap” Means

Building a heap means taking an unordered array and rearranging its values so it follows the heap rule.

In a max heap, every parent node is greater than or equal to its children. The biggest value ends up at the root.

The array itself does not change size. We just swap values around until the order is correct. Remember the index math for an array-based heap:

  • the left child of index i sits at 2*i + 1
  • the right child of index i sits at 2*i + 2
  • the parent of index i sits at (i - 1) / 2

So we never build an actual tree with pointers. The array is the tree.

🧠 The Slow Way vs The Clever Way

The simple idea is to insert each number one at a time into a growing heap. Every insert may travel up the tree to fix the order. That costs O(log n). Do it for n numbers and you get O(n log n).

The clever way is heapify. Instead of going top-down with inserts, we go bottom-up and fix small heaps first.

Here is the key idea in plain words:

  • Leaf nodes have no children. So they are already tiny valid heaps on their own.
  • That means we can skip all the leaves and start from the last node that actually has a child.
  • For each such node we push it down to its correct spot. This is called sift-down.
  • We move backwards toward the root. We fix bigger and bigger sub-heaps as we go.

πŸ”§ What Is Sift-Down?

Sift-down takes one node that might be out of place and pushes it down until the heap rule holds.

The steps for sift-down on index i:

  • Look at node i and its two children.
  • Find the largest among the parent and its children.
  • If the parent is already the largest, stop. Nothing to do.
  • Otherwise swap the parent with the largest child.
  • Now the problem moved down. So repeat sift-down at that child’s index.

Note

We only call sift-down, never sift-up, when building the heap. That is what makes the bottom-up method work.

⚑ Why Bottom-Up Heapify Is O(n)

This part surprises a lot of people. We run sift-down on roughly half the nodes. Each sift-down can cost up to O(log n). So you would guess O(n log n). But the real cost is O(n). Here is the simple reason.

The cost of sift-down depends on the node’s height, not the tree’s height. A node near the bottom can only fall a tiny distance.

  • Most nodes live near the bottom of the tree. They have height 0 or 1, so their sift-down is almost free.
  • Only a few nodes live near the top. They can fall far, but there are very few of them.

So the expensive nodes are rare and the cheap nodes are many. When you add up the work for every node, the total comes out to O(n), not O(n log n).

Tip

Quick intuition: the bottom level holds about half the nodes and each does almost no work. The single root can fall the whole way, but there is only one root. The cheap-but-many part wins.

πŸͺœ Steps to Build a Heap (Heapify)

Here is the full plan before we write any code.

  1. Start with the unordered array of size n.
  2. Find the last non-leaf node. Its index is n / 2 - 1.
  3. Move from that index down to index 0, one step at a time.
  4. At each index, run sift-down to push that value to its correct place.
  5. When the loop reaches the root and finishes, the whole array is a valid max heap.

We start at n / 2 - 1 because every index after it is a leaf. Leaves are already valid heaps. So fixing them would be wasted work.

πŸ’» Heapify in Five Languages

Let us build a max heap from the array [3, 9, 2, 1, 4, 5]. We write a siftDown helper and a buildHeap function that calls it bottom-up.

build_heap.py
# Push the node at index i down to its correct spot
def sift_down(arr, n, i):
while True:
largest = i # assume parent is largest
left = 2 * i + 1 # left child
right = 2 * i + 2 # right child
if left < n and arr[left] > arr[largest]:
largest = left
if right < n and arr[right] > arr[largest]:
largest = right
if largest == i: # parent already largest, done
break
arr[i], arr[largest] = arr[largest], arr[i]
i = largest # continue from where it landed
# Build a max heap from an unordered array
def build_heap(arr):
n = len(arr)
# start at the last non-leaf node, move up to the root
for i in range(n // 2 - 1, -1, -1):
sift_down(arr, n, i)
arr = [3, 9, 2, 1, 4, 5]
build_heap(arr)
print("Max heap:", arr)

The output of the above code will be:

Max heap: [9, 4, 5, 1, 3, 2]

Note

The exact order of the array can differ between valid heaps. What matters is that every parent is greater than or equal to its children. The output above is one correct max heap for this input.

πŸ”Ž A Quick Walk Through The Example

Our array is [3, 9, 2, 1, 4, 5] with n = 6. The last non-leaf index is 6 / 2 - 1 = 2. So we sift down indices 2, 1, 0 in that order.

This diagram shows how the values sit in the tree before we start.

3 (i=0)

9 (i=1)

2 (i=2)

1 (i=3)

4 (i=4)

5 (i=5)

Here is what happens step by step:

  • At index 2, value 2 has child 5. 5 is bigger, so they swap. Index 2 becomes 5.
  • At index 1, value 9 has children 1 and 4. 9 is already the largest, so nothing moves.
  • At index 0, value 3 has children 9 and 5. 9 is largest, so swap. Now 3 sits at index 1 with children 1 and 4. 4 is bigger, so swap again and 3 lands at index 4. Now it has no children below, so it stops.

The root is now 9, the biggest value. The whole array is a valid max heap.

πŸ“Š Time and Space Complexity

Let us compare both ways of building a heap side by side.

Method Time Space
Insert one by one O(n log n) O(1)
Bottom-up heapify O(n) O(1)
One sift-down call O(log n) O(1)

Tip

Bottom-up heapify runs in O(n) time and uses O(1) extra space because it rearranges values inside the same array. That is the fastest way to turn an array into a heap.

⚠️ Common Mistakes

A few small mistakes catch almost everyone the first time.

  • Starting sift-down from index 0 going down. The bottom-up method must start from n / 2 - 1 and move toward the root.
  • Forgetting the child bounds check. Always test left < n and right < n before reading a child, or you read past the array.
  • Stopping sift-down after one swap. After a swap the value may still be out of place lower down, so you must keep going.
  • Mixing up the rule. In a max heap the parent is the largest. For a min heap, flip the comparison and find the smallest.

🧩 What You’ve Learned

βœ… Building a heap means rearranging an unordered array so every parent follows the heap rule.

βœ… The bottom-up heapify method starts at the last non-leaf node n / 2 - 1 and sift-downs each node up to the root.

βœ… Sift-down pushes a node down by swapping it with its largest child until the heap rule holds.

βœ… Heapify runs in O(n) time, which beats inserting nodes one by one at O(n log n).

βœ… It uses O(1) extra space because all the work happens inside the same array.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    Where does bottom-up heapify start sifting down?

    Why: Every index after n / 2 - 1 is a leaf and already a valid heap, so we start at the last non-leaf node and move toward the root.

  2. 2

    What is the time complexity of building a heap with bottom-up heapify?

    Why: Most nodes are near the bottom and barely move, so the total work adds up to O(n) rather than O(n log n).

  3. 3

    Why is heapify O(n) and not O(n log n)?

    Why: Cheap low nodes are many and expensive high nodes are few, so the sum of work is O(n).

  4. 4

    What does sift-down do at one node?

    Why: Sift-down keeps swapping a node with its largest child and moving down until the parent is no longer smaller than its children.

πŸš€ What’s Next?

Share & Connect