Hello! Welcome to the first lesson in our module on Heaps and Priority Queues.
In our previous module, we concluded our study of Binary Search Trees by learning how to construct a balanced BST from a sorted array. We saw how a specific ordering and structure could optimize search operations. Now, we're shifting gears to another powerful tree-based data structure: the heap. While also a tree, its purpose is entirely different. Instead of optimizing for search, a heap is designed to give you constant-time access to the highest-priority (minimum or maximum) element.
Today, we'll focus on the absolute fundamentals. By the end of this lesson, you will be able to explain the core properties of a heap and implement its two fundamental maintenance operations: sift-up and sift-down. Mastering these is the first and most critical step to understanding how heaps and the priority queues built from them work.
1. What is a Heap? The Two Core Properties
A heap is a specialized tree-based data structure that satisfies two crucial properties. Violating either one means it's no longer a valid heap.
Property 1: The Shape Property
A heap must be a complete binary tree. This means that all levels of the tree are completely filled, except possibly for the last level, which is filled from left to right. There are no "gaps" in the tree. This structural constraint is key to a heap's efficient implementation.
Property 2: The Heap Property
This property defines the relationship between a parent node and its children. There are two types of heaps, based on this property:
- Min-Heap: The value of each parent node is less than or equal to the values of its children. This means the smallest element in the entire heap is always at the root.
- Max-Heap: The value of each parent node is greater than or equal to the values of its children. This means the largest element is always at the root.
Throughout this lesson, we will primarily use a min-heap for our examples. The logic for a max-heap is identical, just with the comparisons flipped.

The Array-Based Implementation
Conceptually, a heap is a tree. In practice, however, it's almost always implemented using an array (or a dynamic array/vector). The "complete binary tree" shape property makes this possible and highly efficient.
We can map the tree structure into an array by placing nodes level by level, from left to right. This allows us to find parents and children using simple arithmetic instead of explicit pointers, which is a huge performance and memory win.
Let's watch a short video to see this in action.
This video from HackerRank by Gayle Laakmann McDowell provides a concise overview of the heap property and, more importantly, the array-based representation. This array mapping is a crucial concept for interviews.
First, watch from 00:00 to 00:27 to hear the definition of a min-heap. Then, jump to 02:03 – 03:03 to understand the array implementation and the formulas used to find a node's parent and children. Pay close attention to these formulas, as you'll be using them directly in your code.
As the video explained, for a node at index i (assuming 0-based indexing), we can find its relatives with these formulas:
- Parent:
(i - 1) // 2 - Left Child:
2 * i + 1 - Right Child:
2 * i + 2
Now that we have the fundamentals, let's see how we maintain these properties when the heap changes.
2. Core Operations: Sift-Up and Sift-Down
Whenever we add an element to a heap or remove one, we risk violating the heap property. To fix this, we use two essential operations: sift-up and sift-down.
Heap Operations: Insert, Extract, Heapify
Let's study the logic behind insertion and extraction. This article gives a clear, step-by-step breakdown of the sift-up and sift-down processes.
Read the sections 'Inserting an Element' and 'Extracting the Minimum/Maximum Element'. Focus on the logic: why do we add to the end for an insert? Why do we move the last element to the root for an extract? And how do the sift-up and sift-down operations restore the heap property in each case?
3. Implementing the Core Operations
Let's translate the theory from the article into Python code. We'll build a minimal MinHeap class and implement sift_up and sift_down as internal helper methods. These methods will be the workhorses for the public-facing insert and extract_min methods we'll build in the next lesson.
Sift-Up (for Insertion)
The sift_up operation is used when we insert a new element. The process is:
- Add the new element to the very end of the array. This maintains the shape property.
- The new element might be smaller than its parent, violating the heap property.
- We "sift it up" by repeatedly comparing it to its parent and swapping if it's smaller, until it's in the correct position.

Here is the implementation of sift_up:
class MinHeap:
def __init__(self):
self.heap = []
def _sift_up(self, index):
# We can't sift up the root. The index must be greater than 0.
if index == 0:
return
parent_index = (index - 1) // 2
# If the current node is smaller than its parent, swap them.
if self.heap[index] < self.heap[parent_index]:
self.heap[index], self.heap[parent_index] = self.heap[parent_index], self.heap[index]
# Recursively continue sifting up from the new position.
self._sift_up(parent_index)
Note: A while-loop is often used for an iterative version of this, which avoids recursion depth limits.
Sift-Down (for Extraction)
The sift_down operation is used when we extract the minimum element (the root). The process is:
- The element to be removed is always the root (
heap[0]). - To fill the hole at the root and maintain the shape property, we move the last element from the array to the root.
- This new root is likely out of place, violating the heap property.
- We "sift it down" by repeatedly comparing it with its children and swapping it with the smallest of its children, until it is smaller than both of its children or becomes a leaf node.
Here is the implementation of sift_down:
class MinHeap:
# ... (init and _sift_up from before)
def _sift_down(self, index):
left_child_index = 2 * index + 1
right_child_index = 2 * index + 2
smallest = index # Assume the current node is the smallest
# Check if left child exists and is smaller than current smallest
if left_child_index < len(self.heap) and self.heap[left_child_index] < self.heap[smallest]:
smallest = left_child_index
# Check if right child exists and is smaller than current smallest
if right_child_index < len(self.heap) and self.heap[right_child_index] < self.heap[smallest]:
smallest = right_child_index
# If the smallest element is not the current node, swap them
if smallest != index:
self.heap[index], self.heap[smallest] = self.heap[smallest], self.heap[index]
# Recursively continue sifting down from the new position
self._sift_down(smallest)
Test your understanding!
Suppose you have the following min-heap, represented as an array: [10, 20, 15, 30, 40].
- What would the array look like immediately after inserting the value
5(before any sifting)? - Trace the
_sift_upprocess. What is the final state of the array?
Show answer
-
After insertion, before sifting:
The new element5is added to the end of the array to maintain the complete tree structure.
The array becomes:[10, 20, 15, 30, 40, 5] -
Trace the
_sift_upprocess:- The new element
5is at index5. Its parent is at index(5-1)//2 = 2, which is15. - Since
5 < 15, we swap them. Array:[10, 20, 5, 30, 40, 15]. - The element
5is now at index2. Its new parent is at index(2-1)//2 = 0, which is10. - Since
5 < 10, we swap them. Array:[5, 20, 10, 30, 40, 15]. - The element
5is now at the root (index0). The_sift_upprocess stops.
Final array:
[5, 20, 10, 30, 40, 15] - The new element
Complexity
Both sift_up and sift_down involve a path from the root to a leaf (or vice-versa). The height of a complete binary tree with nodes is . Therefore, the time complexity for both of these core operations is .
Conclusion
Great job! You've just learned the fundamental theory and implementation details of heaps, which are essential for many advanced algorithms and are a frequent topic in technical interviews.
Key Takeaways:
- A heap is defined by two properties: the shape property (it's a complete binary tree) and the heap property (min-heap or max-heap ordering).
- Heaps are almost always implemented with an array for efficiency, using mathematical formulas to navigate the tree structure.
- Sift-up is the operation used to restore the heap property after an insertion by moving an element up the tree.
- Sift-down is the operation used to restore the heap property after an extraction by moving an element down the tree.
In our next lesson, we will use these helper methods to implement a fully functional Priority Queue. We'll see how this data structure can be used to solve a whole new class of problems efficiently.