🧠
Min-Heap Based Approach (Priority Queue)
💡 This approach uses a min-heap to simulate merging sorted rows, similar to merging k sorted lists. It’s intuitive but less efficient than binary search on value range for large matrices.
Intuition
Push the first element of each row into a min-heap. Pop the smallest element and push the next element from the same row. Repeat k times to get the kth smallest.
Algorithm
- Step 1: Initialize a min-heap and push the first element of each row with its coordinates.
- Step 2: Pop the smallest element from the heap.
- Step 3: If there is a next element in the same row, push it into the heap.
- Step 4: Repeat steps 2-3 k times.
- Step 5: The last popped element is the kth smallest.
💡 This approach simulates a k-way merge, which is a common technique for sorted arrays.
from typing import List
import heapq
def kthSmallest(matrix: List[List[int]], k: int) -> int:
n = len(matrix)
min_heap = []
for r in range(min(k, n)):
heapq.heappush(min_heap, (matrix[r][0], r, 0))
count = 0
while min_heap:
val, r, c = heapq.heappop(min_heap)
count += 1
if count == k:
return val
if c + 1 < n:
heapq.heappush(min_heap, (matrix[r][c+1], r, c+1))
# Driver code
if __name__ == '__main__':
matrix = [[1,5,9],[10,11,13],[12,13,15]]
k = 8
print(kthSmallest(matrix, k)) # Output: 13
Line Notes
min_heap = []Initialize a min-heap to store elements with their positions.
for r in range(min(k, n))Push first element of each row up to k rows to limit heap size.
heapq.heappush(min_heap, (matrix[r][0], r, 0))Push tuple (value, row, col) into heap.
val, r, c = heapq.heappop(min_heap)Pop smallest element from heap.
count += 1Increment count of popped elements.
if count == k: return valReturn the kth popped element as answer.
if c + 1 < n:If next element in row exists, push it into heap.
heapq.heappush(min_heap, (matrix[r][c+1], r, c+1))Push next element from same row.
import java.util.*;
public class KthSmallest {
public static int kthSmallest(int[][] matrix, int k) {
int n = matrix.length;
PriorityQueue<int[]> minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
for (int r = 0; r < Math.min(k, n); r++) {
minHeap.offer(new int[]{matrix[r][0], r, 0});
}
int count = 0;
while (!minHeap.isEmpty()) {
int[] curr = minHeap.poll();
int val = curr[0], r = curr[1], c = curr[2];
count++;
if (count == k) return val;
if (c + 1 < n) {
minHeap.offer(new int[]{matrix[r][c + 1], r, c + 1});
}
}
return -1; // Should never reach here
}
public static void main(String[] args) {
int[][] matrix = {{1,5,9},{10,11,13},{12,13,15}};
int k = 8;
System.out.println(kthSmallest(matrix, k)); // Output: 13
}
}
Line Notes
PriorityQueue<int[]> minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));Min-heap ordered by element value.
for (int r = 0; r < Math.min(k, n); r++)Push first element of each row up to k rows.
minHeap.offer(new int[]{matrix[r][0], r, 0});Add element with row and column indices.
int[] curr = minHeap.poll();Pop smallest element from heap.
count++;Increment count of popped elements.
if (count == k) return val;Return kth popped element.
if (c + 1 < n)If next element in row exists, push it.
minHeap.offer(new int[]{matrix[r][c + 1], r, c + 1});Push next element from same row.
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int kthSmallest(vector<vector<int>>& matrix, int k) {
int n = matrix.size();
auto comp = [](const tuple<int,int,int>& a, const tuple<int,int,int>& b) {
return get<0>(a) > get<0>(b);
};
priority_queue<tuple<int,int,int>, vector<tuple<int,int,int>>, decltype(comp)> minHeap(comp);
for (int r = 0; r < min(k, n); ++r) {
minHeap.emplace(matrix[r][0], r, 0);
}
int count = 0;
while (!minHeap.empty()) {
auto [val, r, c] = minHeap.top();
minHeap.pop();
count++;
if (count == k) return val;
if (c + 1 < n) {
minHeap.emplace(matrix[r][c + 1], r, c + 1);
}
}
return -1; // Should not reach here
}
int main() {
vector<vector<int>> matrix = {{1,5,9},{10,11,13},{12,13,15}};
int k = 8;
cout << kthSmallest(matrix, k) << endl; // Output: 13
return 0;
}
Line Notes
auto comp = [](const tuple<int,int,int>& a, const tuple<int,int,int>& b)Comparator for min-heap based on element value.
priority_queue<tuple<int,int,int>, vector<tuple<int,int,int>>, decltype(comp)> minHeap(comp);Declare min-heap with custom comparator.
for (int r = 0; r < min(k, n); ++r)Push first element of each row up to k rows.
minHeap.emplace(matrix[r][0], r, 0);Add element with row and column indices.
auto [val, r, c] = minHeap.top();Get smallest element from heap.
minHeap.pop();Remove smallest element.
count++;Increment count of popped elements.
if (count == k) return val;Return kth popped element.
if (c + 1 < n)If next element in row exists, push it.
minHeap.emplace(matrix[r][c + 1], r, c + 1);Push next element from same row.
class MinHeap {
constructor() {
this.heap = [];
}
swap(i, j) {
[this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];
}
push(val) {
this.heap.push(val);
this.bubbleUp();
}
bubbleUp() {
let idx = this.heap.length - 1;
while (idx > 0) {
let parent = Math.floor((idx - 1) / 2);
if (this.heap[parent][0] <= this.heap[idx][0]) break;
this.swap(parent, idx);
idx = parent;
}
}
pop() {
if (this.heap.length === 1) return this.heap.pop();
const top = this.heap[0];
this.heap[0] = this.heap.pop();
this.bubbleDown();
return top;
}
bubbleDown() {
let idx = 0;
const length = this.heap.length;
while (true) {
let left = 2 * idx + 1;
let right = 2 * idx + 2;
let smallest = idx;
if (left < length && this.heap[left][0] < this.heap[smallest][0]) smallest = left;
if (right < length && this.heap[right][0] < this.heap[smallest][0]) smallest = right;
if (smallest === idx) break;
this.swap(idx, smallest);
idx = smallest;
}
}
size() {
return this.heap.length;
}
}
function kthSmallest(matrix, k) {
const n = matrix.length;
const minHeap = new MinHeap();
for (let r = 0; r < Math.min(k, n); r++) {
minHeap.push([matrix[r][0], r, 0]);
}
let count = 0;
while (minHeap.size() > 0) {
const [val, r, c] = minHeap.pop();
count++;
if (count === k) return val;
if (c + 1 < n) {
minHeap.push([matrix[r][c + 1], r, c + 1]);
}
}
return -1; // Should not reach here
}
// Driver code
const matrix = [[1,5,9],[10,11,13],[12,13,15]];
const k = 8;
console.log(kthSmallest(matrix, k)); // Output: 13
Line Notes
class MinHeapCustom min-heap implementation for priority queue.
push(val)Add new element and maintain heap property.
pop()Remove and return smallest element.
for (let r = 0; r < Math.min(k, n); r++)Push first element of each row up to k rows.
minHeap.push([matrix[r][0], r, 0]);Push element with row and column indices.
const [val, r, c] = minHeap.pop();Pop smallest element from heap.
count++;Increment count of popped elements.
if (count === k) return val;Return kth popped element.
if (c + 1 < n)If next element in row exists, push it.
minHeap.push([matrix[r][c + 1], r, c + 1]);Push next element from same row.
Heap size is at most n, and we perform k extractions and insertions.
💡 For k close to n^2, this can be slower than binary search approach.
Interview Verdict: Accepted but less optimal
This approach is intuitive and useful if k is small, but binary search on value range is preferred for large inputs.