Loading...
Loading...

Priority Queues in DSA: Heap-Based Implementations

Master priority queue operations, heap implementations, and real-world applications. This tutorial covers min/max heaps, Dijkstra's algorithm, and common interview problems with coding examples.

Priority Queue Applications (2023)

Task Scheduling (40%)
Graph Algorithms (30%)
Data Compression (20%)
Other (10%)

1. Priority Queue Fundamentals

Core Principles:

  • Element Priority: Highest/lowest priority served first
  • Key Operations: Insert, Extract-Max/Min, Peek
  • Underlying Structure: Typically implemented with heaps

Time Complexities:

Operation Binary Heap Fibonacci Heap
Insert O(log n) O(1)
Extract-Max/Min O(log n) O(log n)*
Peek O(1) O(1)
Decrease-Key O(log n) O(1)

* Amortized time

Binary Heap Implementation:


class MinHeap {
  constructor() {
    this.heap = [];
  }
  
  insert(val) {
    this.heap.push(val);
    this.bubbleUp(this.heap.length - 1);
  }
  
  extractMin() {
    if (this.isEmpty()) throw new Error("Heap is empty");
    const min = this.heap[0];
    const last = this.heap.pop();
    if (this.heap.length > 0) {
      this.heap[0] = last;
      this.sinkDown(0);
    }
    return min;
  }
  
  bubbleUp(index) {
    while (index > 0) {
      const parentIdx = Math.floor((index - 1) / 2);
      if (this.heap[parentIdx] <= this.heap[index]) break;
      [this.heap[parentIdx], this.heap[index]] = 
        [this.heap[index], this.heap[parentIdx]];
      index = parentIdx;
    }
  }
  
  sinkDown(index) {
    const left = 2 * index + 1;
    const right = 2 * index + 2;
    let smallest = index;
    
    if (left < this.heap.length && this.heap[left] < this.heap[smallest]) {
      smallest = left;
    }
    
    if (right < this.heap.length && this.heap[right] < this.heap[smallest]) {
      smallest = right;
    }
    
    if (smallest !== index) {
      [this.heap[index], this.heap[smallest]] = 
        [this.heap[smallest], this.heap[index]];
      this.sinkDown(smallest);
    }
  }
}

2. Priority Queue Applications

Classic Algorithms:

Dijkstra's Shortest Path


function dijkstra(graph, start) {
  const distances = {};
  const pq = new MinHeap();
  const visited = new Set();
  
  // Initialize distances
  for (const vertex in graph) {
    distances[vertex] = vertex === start ? 0 : Infinity;
    pq.insert({ vertex, distance: distances[vertex] });
  }
  
  while (!pq.isEmpty()) {
    const { vertex: current, distance } = pq.extractMin();
    if (visited.has(current)) continue;
    visited.add(current);
    
    for (const neighbor in graph[current]) {
      const newDistance = distance + graph[current][neighbor];
      if (newDistance < distances[neighbor]) {
        distances[neighbor] = newDistance;
        pq.insert({ vertex: neighbor, distance: newDistance });
      }
    }
  }
  
  return distances;
}

Huffman Coding


class HuffmanNode {
  constructor(char = null, freq = 0, left = null, right = null) {
    this.char = char;
    this.freq = freq;
    this.left = left;
    this.right = right;
  }
}

function buildHuffmanTree(freqMap) {
  const pq = new MinHeap(
    (a, b) => a.freq < b.freq
  );
  
  // Create leaf nodes for each character
  for (const [char, freq] of Object.entries(freqMap)) {
    pq.insert(new HuffmanNode(char, freq));
  }
  
  // Build the tree
  while (pq.size() > 1) {
    const left = pq.extractMin();
    const right = pq.extractMin();
    const merged = new HuffmanNode(null, left.freq + right.freq, left, right);
    pq.insert(merged);
  }
  
  return pq.extractMin();
}

Real-world Use Cases:

  • Hospital Triage: Prioritize patients by severity
  • Network Routing: Prioritize data packets
  • Job Scheduling: OS process prioritization
  • Load Balancing: Prioritize server requests

3. Advanced Heap Techniques

Optimization Patterns:

Heapify Optimization


// Build heap in O(n) time instead of O(n log n)
function heapify(arr) {
  const start = Math.floor(arr.length / 2) - 1;
  for (let i = start; i >= 0; i--) {
    sinkDown(arr, i, arr.length);
  }
  return arr;
}

function sinkDown(arr, index, length) {
  let largest = index;
  const left = 2 * index + 1;
  const right = 2 * index + 2;
  
  if (left < length && arr[left] > arr[largest]) {
    largest = left;
  }
  
  if (right < length && arr[right] > arr[largest]) {
    largest = right;
  }
  
  if (largest !== index) {
    [arr[index], arr[largest]] = [arr[largest], arr[index]];
    sinkDown(arr, largest, length);
  }
}

K-way Merge


function mergeKSortedArrays(arrays) {
  const pq = new MinHeap(
    (a, b) => a.value < b.value
  );
  const result = [];
  
  // Initialize heap with first element of each array
  for (let i = 0; i < arrays.length; i++) {
    if (arrays[i].length > 0) {
      pq.insert({ 
        value: arrays[i][0], 
        arrayIndex: i, 
        elementIndex: 0 
      });
    }
  }
  
  while (!pq.isEmpty()) {
    const { value, arrayIndex, elementIndex } = pq.extractMin();
    result.push(value);
    
    if (elementIndex + 1 < arrays[arrayIndex].length) {
      pq.insert({
        value: arrays[arrayIndex][elementIndex + 1],
        arrayIndex,
        elementIndex: elementIndex + 1
      });
    }
  }
  
  return result;
}

Priority Queue Problem-Solving Cheat Sheet

Problem Type Approach Time Complexity
Top K Elements Min-heap of size K O(n log k)
Merge K Sorted Lists Heap with pointers O(n log k)
Shortest Path Dijkstra's algorithm O(E + V log V)
Stream Median Two heaps (min/max) O(log n) per insert

4. Specialized Priority Queues

Indexed Priority Queue

Supports key updates by index


class IndexedMinPQ {
  constructor() {
    this.values = [];       // values[index] = priority
    this.pm = [];          // position map: pm[index] = heapIndex
    this.im = [];          // inverse map: im[heapIndex] = index
    this.size = 0;
  }
  
  decreaseKey(index, newValue) {
    if (!this.contains(index)) throw new Error("Index does not exist");
    if (newValue >= this.values[index]) return;
    
    this.values[index] = newValue;
    this.swim(this.pm[index]);
  }
  
  contains(index) {
    return this.pm[index] !== undefined;
  }
}

Binomial Heap

Union operation in O(log n)


class BinomialHeap {
  constructor() {
    this.trees = [];
    this.minNode = null;
  }
  
  union(otherHeap) {
    this.trees = [...this.trees, ...otherHeap.trees];
    this.findMin();
  }
  
  findMin() {
    this.minNode = this.trees.reduce((min, tree) => 
      (!min || tree.value < min.value) ? tree : min, null);
  }
}

Double-Ended Priority Queue

Supports both min and max operations


class MinMaxHeap {
  constructor() {
    this.heap = [];
  }
  
  getMin() {
    return this.heap[0];
  }
  
  getMax() {
    if (this.heap.length <= 1) return this.heap[0];
    return Math.max(this.heap[1], 
           this.heap[2] || -Infinity);
  }
}

Priority Queue Problem-Solving Checklist

✓ Choose min-heap vs max-heap based on problem
✓ Consider custom comparator for complex objects
✓ Handle heapify optimization when building from array
✓ Track additional metadata in heap nodes when needed

DSA Expert Insight: Priority queue problems appear in 30% of technical interviews, especially in graph algorithms and system design questions. Mastering heap operations and their variants is crucial for efficient problem-solving.

0 Interaction
0 Views
Views
0 Likes
×
×
×
🍪 CookieConsent@Ptutorials:~

Welcome to Ptutorials

$ Allow cookies on this site ? (y/n)

top-home