Views

Report Content

×

Heaps in DSA: Priority Queues & Heap Algorithms

Master heap operations, heap sort, and priority queue implementations. This tutorial covers min/max heaps, heapify algorithms, and real-world applications with coding examples.

Heap Usage in Systems (2023)

Task Scheduling (45%)
Graph Algorithms (25%)
Memory Management (20%)
Other (10%)

1. Heap Fundamentals

Min-Heap

Definition

Min-Heap is a complete binary tree where the value of each node is less than or equal to the values of its children. The smallest element is always at the root, making it ideal for priority queues where the highest priority is the smallest value.

How It Works

1
Insert (Bubble Up)
  • Add new element at the end of the heap array
  • Compare with parent; swap if smaller
  • Repeat until heap property is restored
2
Extract Min (Sink Down)
  • Remove root (minimum element)
  • Move last element to root
  • Compare with children; swap with smallest child
  • Repeat until heap property is restored
3
Array Representation
  • Parent: (i-1)/2
  • Left Child: 2i+1
  • Right Child: 2i+2
class MinHeap:
    def __init__(self):
        self.heap = []
    
    def insert(self, value):
        self.heap.append(value)
        self._bubble_up(len(self.heap) - 1)
    
    def extract_min(self):
        if not self.heap:
            raise Exception("Heap is empty")
        min_val = self.heap[0]
        last = self.heap.pop()
        if self.heap:
            self.heap[0] = last
            self._sink_down(0)
        return min_val
    
    def _bubble_up(self, index):
        while index > 0:
            parent = (index - 1) // 2
            if self.heap[parent] <= self.heap[index]:
                break
            self.heap[parent], self.heap[index] = self.heap[index], self.heap[parent]
            index = parent
    
    def _sink_down(self, index):
        smallest = index
        left = 2 * index + 1
        right = 2 * index + 2
        
        if left < len(self.heap) and self.heap[left] < self.heap[smallest]:
            smallest = left
        if right < len(self.heap) and self.heap[right] < self.heap[smallest]:
            smallest = right
        
        if smallest != index:
            self.heap[index], self.heap[smallest] = self.heap[smallest], self.heap[index]
            self._sink_down(smallest)
    
    def peek(self):
        return self.heap[0] if self.heap else None
    
    def size(self):
        return len(self.heap)

Complexity: Insert O(log n), Extract O(log n), Peek O(1)
Use When: Need quick access to minimum element

Max-Heap

Definition

Max-Heap is a complete binary tree where the value of each node is greater than or equal to the values of its children. The largest element is always at the root, making it ideal for problems requiring the maximum element.

How It Works

1
Insert (Bubble Up)
  • Add new element at the end
  • Compare with parent; swap if larger
  • Repeat until heap property restored
2
Extract Max (Sink Down)
  • Remove root (maximum element)
  • Move last element to root
  • Compare with children; swap with largest child
  • Repeat until heap property restored
class MaxHeap:
    def __init__(self):
        self.heap = []
    
    def insert(self, value):
        self.heap.append(value)
        self._bubble_up(len(self.heap) - 1)
    
    def extract_max(self):
        if not self.heap:
            raise Exception("Heap is empty")
        max_val = self.heap[0]
        last = self.heap.pop()
        if self.heap:
            self.heap[0] = last
            self._sink_down(0)
        return max_val
    
    def _bubble_up(self, index):
        while index > 0:
            parent = (index - 1) // 2
            if self.heap[parent] >= self.heap[index]:
                break
            self.heap[parent], self.heap[index] = self.heap[index], self.heap[parent]
            index = parent
    
    def _sink_down(self, index):
        largest = index
        left = 2 * index + 1
        right = 2 * index + 2
        
        if left < len(self.heap) and self.heap[left] > self.heap[largest]:
            largest = left
        if right < len(self.heap) and self.heap[right] > self.heap[largest]:
            largest = right
        
        if largest != index:
            self.heap[index], self.heap[largest] = self.heap[largest], self.heap[index]
            self._sink_down(largest)
    
    def peek(self):
        return self.heap[0] if self.heap else None

2. Heap Operations & Algorithms

Heap Sort

Definition

Heap Sort is a comparison-based sorting algorithm that uses a binary heap data structure. It divides the array into sorted and unsorted regions and repeatedly extracts the maximum/minimum element from the heap.

How It Works

1
Build Max Heap
  • Start from last non-leaf node
  • Heapify each node to satisfy heap property
  • Result: Largest element at root
2
Extract & Swap
  • Swap root (largest) with last element
  • Reduce heap size by 1
  • Heapify the reduced heap
3
Repeat
  • Continue until heap size becomes 1
  • Array becomes sorted in ascending order
def heap_sort(arr):
    n = len(arr)
    
    # Build max heap
    for i in range(n // 2 - 1, -1, -1):
        heapify(arr, n, i)
    
    # Extract elements one by one
    for i in range(n - 1, 0, -1):
        arr[0], arr[i] = arr[i], arr[0]  # Swap
        heapify(arr, i, 0)
    
    return arr

def heapify(arr, n, i):
    largest = i
    left = 2 * i + 1
    right = 2 * i + 2
    
    if left < n and arr[left] > arr[largest]:
        largest = left
    if right < n and arr[right] > arr[largest]:
        largest = right
    
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)

Complexity: O(n log n) time, O(1) space
Use When: In-place sorting with guaranteed O(n log n)

Top K Elements

Definition

Top K Elements problem finds the K largest or smallest elements in an array using a min-heap or max-heap. This approach is optimal for large datasets.

How It Works

1
Initialize Min-Heap
  • Create min-heap of size K
  • Insert first K elements
2
Process Remaining
  • For each element, compare with heap root
  • If larger than root, remove root and insert element
3
Extract Result
  • Heap contains K largest elements
  • Elements remain in heap order
import heapq

def top_k_elements(nums, k):
    min_heap = []
    
    for num in nums:
        heapq.heappush(min_heap, num)
        if len(min_heap) > k:
            heapq.heappop(min_heap)
    
    return min_heap

# For Kth largest element
def kth_largest(nums, k):
    min_heap = []
    for num in nums:
        heapq.heappush(min_heap, num)
        if len(min_heap) > k:
            heapq.heappop(min_heap)
    return min_heap[0]

Complexity: O(n log k) time, O(k) space
Use When: Finding top/largest K elements

3. Heapify & Heap Construction

Heapify (Build Heap)

Definition

Heapify is the process of converting an arbitrary array into a heap in O(n) time. It works by applying the sink-down operation on non-leaf nodes from bottom to top.

How It Works

1
Start from last non-leaf
  • Index = (n/2) - 1
  • All leaf nodes are already valid heaps
2
Apply sink-down
  • For each node, ensure it's >= children
  • Swap if needed and recurse down
3
Move upward
  • Continue for all indices down to 0
  • Result: Complete heap structure
def heapify(arr, n, i):
    largest = i
    left = 2 * i + 1
    right = 2 * i + 2
    
    if left < n and arr[left] > arr[largest]:
        largest = left
    if right < n and arr[right] > arr[largest]:
        largest = right
    
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)

def build_heap(arr):
    n = len(arr)
    # Start from last non-leaf node
    for i in range(n // 2 - 1, -1, -1):
        heapify(arr, n, i)
    return arr

Complexity: O(n) time, O(log n) recursion stack
Use When: Converting array to heap efficiently

4. Real-World Applications

Priority Queue (Task Scheduler)

Task Scheduling: Operating systems use heaps to schedule processes by priority

import heapq

class TaskScheduler:
    def __init__(self):
        self.heap = []  # (priority, task)
    
    def add_task(self, task, priority):
        heapq.heappush(self.heap, (priority, task))
    
    def execute_task(self):
        if self.heap:
            priority, task = heapq.heappop(self.heap)
            return task
        return None

Dijkstra's Shortest Path

Graph Algorithms: Heaps optimize distance calculations

import heapq

def dijkstra(graph, start):
    distances = {node: float('inf') for node in graph}
    distances[start] = 0
    pq = [(0, start)]
    
    while pq:
        current_dist, node = heapq.heappop(pq)
        
        if current_dist > distances[node]:
            continue
        
        for neighbor, weight in graph[node]:
            distance = current_dist + weight
            if distance < distances[neighbor]:
                distances[neighbor] = distance
                heapq.heappush(pq, (distance, neighbor))
    
    return distances

Median Maintenance

Two-Heap Approach: Track median in a data stream

import heapq

class MedianFinder:
    def __init__(self):
        self.max_heap = []  # store smaller half (negated for max-heap)
        self.min_heap = []  # store larger half
    
    def add_num(self, num):
        heapq.heappush(self.max_heap, -num)
        heapq.heappush(self.min_heap, -heapq.heappop(self.max_heap))
        
        if len(self.min_heap) > len(self.max_heap):
            heapq.heappush(self.max_heap, -heapq.heappop(self.min_heap))
    
    def find_median(self):
        if len(self.max_heap) > len(self.min_heap):
            return -self.max_heap[0]
        return (-self.max_heap[0] + self.min_heap[0]) / 2

Heap Problem-Solving Cheat Sheet

Problem Type Approach Time Complexity
Kth Largest Element Min-heap of size K O(n log k)
Merge K Sorted Arrays Min-heap with pointers O(n log k)
Sliding Window Median Two heaps (min + max) O(n log k)
Huffman Coding Min-heap for tree construction O(n log n)
Dijkstra's Algorithm Min-heap for distances O(E log V)

Heap Problem-Solving Checklist

Choose min-heap vs max-heap based on problem requirements
Consider heap size for K-element problems (keep heap size = K)
Utilize heapify for O(n) construction instead of n insertions
Track additional node data when comparing custom objects
Use two-heap pattern for median tracking problems
Consider lazy deletion for dynamic updates

DSA Expert Insight: Heap problems appear in 30% of technical interviews, especially in priority queue and top-K element scenarios. Mastering both basic operations and specialized heap variants is key to efficient problem-solving. The two-heap pattern is particularly powerful for streaming median problems.

Share and Join the Discussion

You need to be logged in to participate in this discussion.

×
×