🧠
Better Approach (Merge with Min-Heap)
💡 This approach uses a min-heap to efficiently track the smallest current element among the lists, enabling a sliding window over the merged elements to find the smallest range.
Intuition
Merge the k sorted lists using a min-heap, always knowing the current minimum element. Track the maximum element seen so far to maintain a valid range covering all lists.
Algorithm
- Initialize a min-heap and insert the first element of each list along with its list index and element index.
- Track the current maximum element among the inserted elements.
- Pop the smallest element from the heap and update the range if the current max - min is smaller.
- If the popped element's list has more elements, insert the next element into the heap and update the max if needed.
- Repeat until one list is exhausted.
💡 The key is maintaining a window that always contains at least one element from each list, using the heap to efficiently find the minimum.
import heapq
def smallestRange(nums):
k = len(nums)
min_heap = []
current_max = float('-inf')
# Initialize heap with first element of each list
for i in range(k):
if nums[i]:
val = nums[i][0]
heapq.heappush(min_heap, (val, i, 0))
current_max = max(current_max, val)
range_start, range_end = float('-inf'), float('inf')
while len(min_heap) == k:
current_min, row, idx = heapq.heappop(min_heap)
if current_max - current_min < range_end - range_start or (current_max - current_min == range_end - range_start and current_min < range_start):
range_start, range_end = current_min, current_max
if idx + 1 < len(nums[row]):
next_val = nums[row][idx + 1]
heapq.heappush(min_heap, (next_val, row, idx + 1))
current_max = max(current_max, next_val)
else:
break
return [range_start, range_end]
# Driver code
if __name__ == '__main__':
nums = [[4,10,15,24,26],[0,9,12,20],[5,18,22,30]]
print(smallestRange(nums))
Line Notes
import heapqUse heapq for efficient min-heap operations
for i in range(k)Initialize heap with first element from each list
heapq.heappush(min_heap, (val, i, 0))Push tuple with value, list index, and element index
current_max = max(current_max, val)Track the maximum element among current heap elements
while len(min_heap) == kContinue while heap contains one element from each list
current_min, row, idx = heapq.heappop(min_heap)Pop smallest element to update range
if idx + 1 < len(nums[row])If more elements in the same list, push next element
current_max = max(current_max, next_val)Update current max if next element is larger
import java.util.*;
public class SmallestRangeHeap {
public static int[] smallestRange(List<List<Integer>> nums) {
PriorityQueue<int[]> minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
int currentMax = Integer.MIN_VALUE;
int k = nums.size();
for (int i = 0; i < k; i++) {
if (!nums.get(i).isEmpty()) {
int val = nums.get(i).get(0);
minHeap.offer(new int[]{val, i, 0});
currentMax = Math.max(currentMax, val);
}
}
int rangeStart = Integer.MIN_VALUE, rangeEnd = Integer.MAX_VALUE;
while (minHeap.size() == k) {
int[] minElem = minHeap.poll();
int currentMin = minElem[0], row = minElem[1], idx = minElem[2];
if (currentMax - currentMin < rangeEnd - rangeStart || (currentMax - currentMin == rangeEnd - rangeStart && currentMin < rangeStart)) {
rangeStart = currentMin;
rangeEnd = currentMax;
}
if (idx + 1 < nums.get(row).size()) {
int nextVal = nums.get(row).get(idx + 1);
minHeap.offer(new int[]{nextVal, row, idx + 1});
currentMax = Math.max(currentMax, nextVal);
} else {
break;
}
}
return new int[]{rangeStart, rangeEnd};
}
public static void main(String[] args) {
List<List<Integer>> nums = Arrays.asList(
Arrays.asList(4,10,15,24,26),
Arrays.asList(0,9,12,20),
Arrays.asList(5,18,22,30)
);
int[] res = smallestRange(nums);
System.out.println("[" + res[0] + "," + res[1] + "]");
}
}
Line Notes
PriorityQueue<int[]> minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));Min-heap ordered by element value
for (int i = 0; i < k; i++)Initialize heap with first element from each list
minHeap.offer(new int[]{val, i, 0});Store value, list index, and element index
while (minHeap.size() == k)Process while all lists are represented in heap
int[] minElem = minHeap.poll();Extract smallest element to update range
if (idx + 1 < nums.get(row).size())Add next element from same list if available
currentMax = Math.max(currentMax, nextVal);Update max value seen so far
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;
struct Element {
int val, row, idx;
bool operator>(const Element& other) const {
return val > other.val;
}
};
pair<int,int> smallestRange(const vector<vector<int>>& nums) {
int k = nums.size();
priority_queue<Element, vector<Element>, greater<Element>> minHeap;
int currentMax = INT_MIN;
for (int i = 0; i < k; i++) {
if (!nums[i].empty()) {
minHeap.push({nums[i][0], i, 0});
currentMax = max(currentMax, nums[i][0]);
}
}
int rangeStart = INT_MIN, rangeEnd = INT_MAX;
while ((int)minHeap.size() == k) {
Element minElem = minHeap.top(); minHeap.pop();
int currentMin = minElem.val, row = minElem.row, idx = minElem.idx;
if (currentMax - currentMin < rangeEnd - rangeStart || (currentMax - currentMin == rangeEnd - rangeStart && currentMin < rangeStart)) {
rangeStart = currentMin;
rangeEnd = currentMax;
}
if (idx + 1 < (int)nums[row].size()) {
int nextVal = nums[row][idx + 1];
minHeap.push({nextVal, row, idx + 1});
currentMax = max(currentMax, nextVal);
} else {
break;
}
}
return {rangeStart, rangeEnd};
}
int main() {
vector<vector<int>> nums = {{4,10,15,24,26},{0,9,12,20},{5,18,22,30}};
auto res = smallestRange(nums);
cout << "[" << res.first << "," << res.second << "]" << endl;
return 0;
}
Line Notes
struct ElementCustom struct to hold value and indices for heap
bool operator>(const Element& other) constDefine comparison for min-heap ordering
priority_queue<Element, vector<Element>, greater<Element>> minHeap;Min-heap of Elements
for (int i = 0; i < k; i++)Initialize heap with first element from each list
while ((int)minHeap.size() == k)Continue while all lists are represented
Element minElem = minHeap.top(); minHeap.pop();Pop smallest element to update range
if (idx + 1 < (int)nums[row].size())Push next element from same list if available
currentMax = max(currentMax, nextVal);Update max value seen so far
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(this.heap.length - 1);
}
bubbleUp(idx) {
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(0);
return top;
}
bubbleDown(idx) {
const length = this.heap.length;
while (true) {
let left = 2 * idx + 1, right = 2 * idx + 2, 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 smallestRange(nums) {
const k = nums.length;
const minHeap = new MinHeap();
let currentMax = -Infinity;
for (let i = 0; i < k; i++) {
if (nums[i].length > 0) {
minHeap.push([nums[i][0], i, 0]);
currentMax = Math.max(currentMax, nums[i][0]);
}
}
let rangeStart = -Infinity, rangeEnd = Infinity;
while (minHeap.size() === k) {
const [currentMin, row, idx] = minHeap.pop();
if (currentMax - currentMin < rangeEnd - rangeStart || (currentMax - currentMin === rangeEnd - rangeStart && currentMin < rangeStart)) {
rangeStart = currentMin;
rangeEnd = currentMax;
}
if (idx + 1 < nums[row].length) {
const nextVal = nums[row][idx + 1];
minHeap.push([nextVal, row, idx + 1]);
currentMax = Math.max(currentMax, nextVal);
} else {
break;
}
}
return [rangeStart, rangeEnd];
}
// Test
const nums = [[4,10,15,24,26],[0,9,12,20],[5,18,22,30]];
console.log(smallestRange(nums));
Line Notes
class MinHeapCustom min-heap implementation since JS lacks built-in heap
push(val)Add element and maintain heap property by bubbling up
pop()Remove smallest element and restore heap by bubbling down
for (let i = 0; i < k; i++)Initialize heap with first element from each list
while (minHeap.size() === k)Process while all lists are represented in heap
const [currentMin, row, idx] = minHeap.pop();Extract smallest element to update range
if (idx + 1 < nums[row].length)Add next element from same list if available
currentMax = Math.max(currentMax, nextVal);Update max value seen so far
TimeO(n log k) where n is total elements and k is number of lists
SpaceO(k) for the heap and pointers
Each element is pushed and popped once from the heap of size k, so operations cost O(log k) each.
💡 For k=3 and total n=12, this means about 12*log3 ≈ 19 operations, much faster than brute force.
Interview Verdict: Accepted
This approach is efficient and practical for large inputs, making it the standard solution in interviews.