HeapProblem 11 of 18
Smallest range in K Lists
Problem Statement
Given K sorted lists of integers, find the smallest range that includes at least one element from each of the K lists.
Example:
- Input:
lists = [[4, 10, 15, 24, 26], [0, 9, 12, 20], [5, 18, 22, 30]] - Output:
[20, 24] - Explanation:
- List 1: [4, 10, 15, 24, 26], 24 is in range
- List 2: [0, 9, 12, 20], 20 is in range
- List 3: [5, 18, 22, 30], 22 is in range
- Range [20, 24] covers all three lists with minimum span
Approach 1: Check All Possible Ranges (Brute Force)
Intuition
For each element in each list, consider it as the start of the range. Find the minimum range that includes at least one element from every other list.
Algorithm
- Flatten all lists and sort by value
- Use sliding window to find minimum range containing K different lists
- Track which lists are covered in current window
java
import java.util.*;
public class Solution {
public int[] smallestRange(List<List<Integer>> nums) {
List<int[]> elements = new ArrayList<>(); // {value, list index}
int k = nums.size();
// Flatten all lists
for (int i = 0; i < k; i++) {
for (int num : nums.get(i)) {
elements.add(new int[]{num, i});
}
}
// Sort by value
elements.sort((a, b) -> a[0] - b[0]);
int n = elements.size();
int[] count = new int[k];
int listsInRange = 0;
int left = 0;
int minRange = Integer.MAX_VALUE;
int rangeStart = 0, rangeEnd = 0;
for (int right = 0; right < n; right++) {
// Add element
int listIdx = elements.get(right)[1];
if (count[listIdx] == 0) {
listsInRange++;
}
count[listIdx]++;
// Shrink window
while (listsInRange == k) {
int currentRange = elements.get(right)[0] - elements.get(left)[0];
if (currentRange < minRange) {
minRange = currentRange;
rangeStart = elements.get(left)[0];
rangeEnd = elements.get(right)[0];
}
int leftListIdx = elements.get(left)[1];
count[leftListIdx]--;
if (count[leftListIdx] == 0) {
listsInRange--;
}
left++;
}
}
return new int[]{rangeStart, rangeEnd};
}
}Complexity Analysis
- Time Complexity: O(NK log(NK)) - Sorting all elements
- Space Complexity: O(N*K) - Storing all elements
Approach 2: Min-Heap (Optimal)
Intuition
Maintain pointers to one element from each list. The range is from minimum to maximum of current elements. Use min-heap to find minimum efficiently. Advance the pointer of minimum element and update range.
Algorithm
- Initialize pointers to first element of each list
- Build min-heap with (value, list index, element index)
- Track current maximum
- Range = [heap.min, currentMax]
- Pop minimum, advance its pointer, push next element
- Update maximum and range as needed
- Stop when any list is exhausted
java
import java.util.*;
public class Solution {
public int[] smallestRange(List<List<Integer>> nums) {
int k = nums.size();
// Min-heap: {value, list index, element index}
PriorityQueue<int[]> minHeap = new PriorityQueue<>(
(a, b) -> a[0] - b[0]
);
int currentMax = Integer.MIN_VALUE;
// Initialize with first element of each list
for (int i = 0; i < k; i++) {
int val = nums.get(i).get(0);
minHeap.offer(new int[]{val, i, 0});
currentMax = Math.max(currentMax, val);
}
int rangeStart = 0, rangeEnd = Integer.MAX_VALUE;
while (true) {
// Get minimum element
int[] curr = minHeap.poll();
int minVal = curr[0];
int listIdx = curr[1];
int elemIdx = curr[2];
// Update range if smaller
if (currentMax - minVal < rangeEnd - rangeStart) {
rangeStart = minVal;
rangeEnd = currentMax;
}
// Move to next element
if (elemIdx + 1 >= nums.get(listIdx).size()) {
break;
}
int nextVal = nums.get(listIdx).get(elemIdx + 1);
minHeap.offer(new int[]{nextVal, listIdx, elemIdx + 1});
currentMax = Math.max(currentMax, nextVal);
}
return new int[]{rangeStart, rangeEnd};
}
}Complexity Analysis
- Time Complexity: O(N*K log K) - Each element is pushed/popped once, heap has K elements
- Space Complexity: O(K) - Heap stores K elements
Approach 3: Two Pointers with Sorting
Intuition
Similar to sliding window but with explicit tracking of which elements from each list are included.
Algorithm
- Merge all lists into sorted array with list indices
- Use two pointers to find minimum window containing all lists
- Track count of elements from each list in window
java
import java.util.*;
public class Solution {
public int[] smallestRange(List<List<Integer>> nums) {
int k = nums.size();
// Create merged sorted list
List<int[]> merged = new ArrayList<>();
for (int i = 0; i < k; i++) {
for (int num : nums.get(i)) {
merged.add(new int[]{num, i});
}
}
merged.sort((a, b) -> a[0] - b[0]);
// Two pointers
Map<Integer, Integer> listCount = new HashMap<>();
int uniqueLists = 0;
int left = 0;
int minLen = Integer.MAX_VALUE;
int rangeStart = 0, rangeEnd = 0;
for (int right = 0; right < merged.size(); right++) {
int listIdx = merged.get(right)[1];
listCount.put(listIdx, listCount.getOrDefault(listIdx, 0) + 1);
if (listCount.get(listIdx) == 1) {
uniqueLists++;
}
// Shrink window
while (uniqueLists == k) {
int len = merged.get(right)[0] - merged.get(left)[0];
if (len < minLen) {
minLen = len;
rangeStart = merged.get(left)[0];
rangeEnd = merged.get(right)[0];
}
int leftListIdx = merged.get(left)[1];
listCount.put(leftListIdx, listCount.get(leftListIdx) - 1);
if (listCount.get(leftListIdx) == 0) {
uniqueLists--;
}
left++;
}
}
return new int[]{rangeStart, rangeEnd};
}
}Complexity Analysis
- Time Complexity: O(NK log(NK)) - Sorting dominates
- Space Complexity: O(N*K) - Storing merged array
Key Takeaways
- Min-Heap approach is space-optimal - O(K) space
- Heap maintains current element from each list efficiently
- Range is always [min of current elements, max of current elements]
- Stop when any list is exhausted (can't cover all lists anymore)
- This is LeetCode 632 - a classic heap problem
- Sliding window on merged array is also valid but uses more space
- Key insight: Advancing minimum element is the only way to potentially shrink range
- Similar to merge K sorted lists but with range tracking