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 Queues in DSA: Heap-Based Implementations
Priority Queue Applications (2023)
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.
×