632. Smallest Range Covering Elements from K Lists
You have k lists of sorted integers in non-decreasing order. Find the smallest range that includes at least one number from each of the k lists.
We define the range [a, b] is smaller than range [c, d] if b - a < d - c or a < c if b - a == d - c.
Example 1:
Input: nums = [[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 [20,24].
List 2: [0, 9, 12, 20], 20 is in range [20,24].
List 3: [5, 18, 22, 30], 22 is in range [20,24].
Example 2:
Input: nums = [[1,2,3],[1,2,3],[1,2,3]]
Output: [1,1]
Constraints:
- nums.length == k
- 1 <= k <= 3500
- 1 <= nums[i].length <= 50
- − 1 0 5 < = n u m s [ i ] [ j ] < = 1 0 5 -10^5 <= nums[i][j] <= 10^5 −105<=nums[i][j]<=105
- nums[i] is sorted in non-decreasing order.
From: LeetCode
Link: 632. Smallest Range Covering Elements from K Lists
Solution:
Ideas:
- Use a Min-Heap (Priority Queue): Keeps track of the smallest element across the k lists.
- Track Maximum Element: Maintains the largest value in the current range.
- Heap Operations (O(log k)):
- Push: Insert the next element from the same list.
- Pop: Remove the smallest element and update the range.
- Stop Early: If any list is exhausted, we stop (ensuring all k lists are covered).
- Time Complexity: O(N log k) (N = total elements, k = number of lists).
- Efficient Heapify: Uses a binary heap instead of qsort() for faster heap operations.
- Result: Finds the smallest range covering at least one number from each list.
Code:
/* Structure for min-heap nodes */
typedef struct {
int value;
int row;
int col;
} HeapNode;
/* Min-heap functions */
void swap(HeapNode *a, HeapNode *b) {
HeapNode temp = *a;
*a = *b;
*b = temp;
}
void heapify(HeapNode heap[], int size, int i) {
int smallest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < size && heap[left].value < heap[smallest].value)
smallest = left;
if (right < size && heap[right].value < heap[smallest].value)
smallest = right;
if (smallest != i) {
swap(&heap[i], &heap[smallest]);
heapify(heap, size, smallest);
}
}
void heapPush(HeapNode heap[], int *size, HeapNode node) {
int i = (*size)++;
heap[i] = node;
while (i > 0 && heap[i].value < heap[(i - 1) / 2].value) {
swap(&heap[i], &heap[(i - 1) / 2]);
i = (i - 1) / 2;
}
}
HeapNode heapPop(HeapNode heap[], int *size) {
HeapNode minNode = heap[0];
heap[0] = heap[--(*size)];
heapify(heap, *size, 0);
return minNode;
}
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* smallestRange(int** nums, int numsSize, int* numsColSize, int* returnSize) {
int *result = (int*)malloc(2 * sizeof(int));
int rangeStart = 0, rangeEnd = INT_MAX;
/* Create min-heap */
HeapNode *heap = (HeapNode*)malloc(numsSize * sizeof(HeapNode));
int heapSize = 0;
/* Track max value in heap */
int maxVal = INT_MIN;
/* Initialize heap with the first element of each list */
for (int i = 0; i < numsSize; i++) {
heap[heapSize].value = nums[i][0];
heap[heapSize].row = i;
heap[heapSize].col = 0;
maxVal = (maxVal > nums[i][0]) ? maxVal : nums[i][0];
heapSize++;
}
/* Heapify */
for (int i = heapSize / 2 - 1; i >= 0; i--)
heapify(heap, heapSize, i);
/* Main loop to find the smallest range */
while (1) {
HeapNode minNode = heapPop(heap, &heapSize);
int minVal = minNode.value;
/* Update range if the current range is smaller */
if ((maxVal - minVal) < (rangeEnd - rangeStart)) {
rangeStart = minVal;
rangeEnd = maxVal;
}
/* Move to the next element in the same list */
int nextCol = minNode.col + 1;
if (nextCol < numsColSize[minNode.row]) {
HeapNode nextNode;
nextNode.value = nums[minNode.row][nextCol];
nextNode.row = minNode.row;
nextNode.col = nextCol;
maxVal = (maxVal > nextNode.value) ? maxVal : nextNode.value;
heapPush(heap, &heapSize, nextNode);
} else {
break; // Stop if we reach the end of one list
}
}
/* Set result and return */
result[0] = rangeStart;
result[1] = rangeEnd;
*returnSize = 2;
free(heap);
return result;
}