🧠
Better Approach: Min-Heap K-way Merge
💡 This approach improves efficiency by merging arrays using a min-heap, avoiding full sorting. It is a classic technique for merging K sorted arrays and helps find the median by extracting elements in sorted order until the median position.
Intuition
Use a min-heap to always extract the smallest current element among the arrays, simulating a merge process without sorting all elements at once.
Algorithm
- Calculate total number of elements across all arrays.
- Initialize a min-heap and push the first element of each non-empty array along with array and element indices.
- Pop elements from the heap one by one, pushing the next element from the same array when available.
- Stop when the median position(s) is reached and compute the median.
💡 The challenge is managing indices and heap operations correctly to simulate the merge without full sorting.
import heapq
def find_median_k_sorted_arrays(arrays):
min_heap = []
total_len = sum(len(arr) for arr in arrays)
for i, arr in enumerate(arrays):
if arr:
heapq.heappush(min_heap, (arr[0], i, 0))
if total_len == 0:
raise ValueError('No elements to find median')
mid1 = (total_len - 1) // 2
mid2 = total_len // 2
count = -1
median1 = median2 = None
while min_heap:
val, arr_idx, ele_idx = heapq.heappop(min_heap)
count += 1
if count == mid1:
median1 = val
if count == mid2:
median2 = val
break
if ele_idx + 1 < len(arrays[arr_idx]):
next_val = arrays[arr_idx][ele_idx + 1]
heapq.heappush(min_heap, (next_val, arr_idx, ele_idx + 1))
if total_len % 2 == 1:
return float(median2)
else:
return (median1 + median2) / 2
# Driver code
if __name__ == '__main__':
arrays = [[1,3,5],[2,4,6],[7,8,9]]
print(find_median_k_sorted_arrays(arrays)) # Output: 5.0
Line Notes
min_heap = []Initialize min-heap to store current smallest elements
total_len = sum(len(arr) for arr in arrays)Calculate total number of elements across all arrays
for i, arr in enumerate(arrays):Iterate over each array with index
if arr:Only push if array is non-empty
heapq.heappush(min_heap, (arr[0], i, 0))Push first element with array and element indices
mid1 = (total_len - 1) // 2Calculate first median index (for even length)
mid2 = total_len // 2Calculate second median index
while min_heap:Extract elements from heap until median found
val, arr_idx, ele_idx = heapq.heappop(min_heap)Pop smallest element and track its origin
count += 1Count how many elements popped so far
if count == mid1: median1 = valRecord first median candidate
if count == mid2: median2 = val; breakRecord second median candidate and stop
if ele_idx + 1 < len(arrays[arr_idx]):If next element exists in same array, push it
heapq.heappush(min_heap, (next_val, arr_idx, ele_idx + 1))Push next element from same array
import java.util.*;
public class MedianKSortedArrays {
static class Element implements Comparable<Element> {
int val, arrIdx, eleIdx;
Element(int v, int a, int e) { val = v; arrIdx = a; eleIdx = e; }
public int compareTo(Element other) { return Integer.compare(this.val, other.val); }
}
public static double findMedian(List<List<Integer>> arrays) {
PriorityQueue<Element> minHeap = new PriorityQueue<>();
int totalLen = 0;
for (List<Integer> arr : arrays) {
totalLen += arr.size();
}
for (int i = 0; i < arrays.size(); i++) {
List<Integer> arr = arrays.get(i);
if (!arr.isEmpty()) {
minHeap.offer(new Element(arr.get(0), i, 0));
}
}
if (totalLen == 0) throw new IllegalArgumentException("No elements to find median");
int mid1 = (totalLen - 1) / 2;
int mid2 = totalLen / 2;
int count = -1;
int median1 = 0, median2 = 0;
while (!minHeap.isEmpty()) {
Element e = minHeap.poll();
count++;
if (count == mid1) median1 = e.val;
if (count == mid2) {
median2 = e.val;
break;
}
List<Integer> arr = arrays.get(e.arrIdx);
if (e.eleIdx + 1 < arr.size()) {
minHeap.offer(new Element(arr.get(e.eleIdx + 1), e.arrIdx, e.eleIdx + 1));
}
}
if (totalLen % 2 == 1) return (double) median2;
else return (median1 + median2) / 2.0;
}
public static void main(String[] args) {
List<List<Integer>> arrays = Arrays.asList(
Arrays.asList(1,3,5),
Arrays.asList(2,4,6),
Arrays.asList(7,8,9)
);
System.out.println(findMedian(arrays)); // Output: 5.0
}
}
Line Notes
static class Element implements Comparable<Element>Define a class to store value and indices for heap ordering
int totalLen = 0;Initialize total length counter
for (List<Integer> arr : arrays)Sum lengths of all arrays
for (int i = 0; i < arrays.size(); i++)Iterate over each array to push first elements
if (!arr.isEmpty())Only add if array is non-empty
minHeap.offer(new Element(arr.get(0), i, 0));Add first element of each array to heap
int mid1 = (totalLen - 1) / 2;Calculate first median index
int mid2 = totalLen / 2;Calculate second median index
while (!minHeap.isEmpty())Extract elements until median found
Element e = minHeap.poll();Pop smallest element
count++;Count elements popped
if (count == mid1) median1 = e.val;Record first median candidate
if (count == mid2)Record second median candidate and break
if (e.eleIdx + 1 < arr.size())If next element exists, add to heap
minHeap.offer(new Element(arr.get(e.eleIdx + 1), e.arrIdx, e.eleIdx + 1));Push next element from same array
#include <iostream>
#include <vector>
#include <queue>
#include <stdexcept>
using namespace std;
struct Element {
int val, arrIdx, eleIdx;
bool operator>(const Element& other) const {
return val > other.val;
}
};
double findMedianKSortedArrays(const vector<vector<int>>& arrays) {
priority_queue<Element, vector<Element>, greater<Element>> minHeap;
int totalLen = 0;
for (const auto& arr : arrays) {
totalLen += arr.size();
}
for (int i = 0; i < arrays.size(); i++) {
if (!arrays[i].empty()) {
minHeap.push({arrays[i][0], i, 0});
}
}
if (totalLen == 0) throw invalid_argument("No elements to find median");
int mid1 = (totalLen - 1) / 2;
int mid2 = totalLen / 2;
int count = -1;
int median1 = 0, median2 = 0;
while (!minHeap.empty()) {
Element e = minHeap.top(); minHeap.pop();
count++;
if (count == mid1) median1 = e.val;
if (count == mid2) {
median2 = e.val;
break;
}
if (e.eleIdx + 1 < arrays[e.arrIdx].size()) {
minHeap.push({arrays[e.arrIdx][e.eleIdx + 1], e.arrIdx, e.eleIdx + 1});
}
}
if (totalLen % 2 == 1) return (double)median2;
else return (median1 + median2) / 2.0;
}
int main() {
vector<vector<int>> arrays = {{1,3,5},{2,4,6},{7,8,9}};
cout << findMedianKSortedArrays(arrays) << endl; // Output: 5.0
return 0;
}
Line Notes
struct ElementStruct to hold value and indices for heap ordering
int totalLen = 0;Initialize total length counter
for (const auto& arr : arrays)Sum lengths of all arrays
for (int i = 0; i < arrays.size(); i++)Iterate over each array to push first elements
if (!arrays[i].empty())Only add if array is non-empty
minHeap.push({arrays[i][0], i, 0});Push first element of each array
int mid1 = (totalLen - 1) / 2;Calculate first median index
int mid2 = totalLen / 2;Calculate second median index
while (!minHeap.empty())Extract elements until median found
Element e = minHeap.top(); minHeap.pop();Pop smallest element
count++;Count elements popped
if (count == mid1) median1 = e.val;Record first median candidate
if (count == mid2)Record second median candidate and break
if (e.eleIdx + 1 < arrays[e.arrIdx].size())If next element exists, push it
minHeap.push({arrays[e.arrIdx][e.eleIdx + 1], e.arrIdx, e.eleIdx + 1});Push next element from same array
class Element {
constructor(val, arrIdx, eleIdx) {
this.val = val;
this.arrIdx = arrIdx;
this.eleIdx = eleIdx;
}
}
function findMedianKSortedArrays(arrays) {
const minHeap = [];
function heapPush(e) {
minHeap.push(e);
let i = minHeap.length - 1;
while (i > 0) {
let p = Math.floor((i - 1) / 2);
if (minHeap[p].val <= minHeap[i].val) break;
[minHeap[p], minHeap[i]] = [minHeap[i], minHeap[p]];
i = p;
}
}
function heapPop() {
const top = minHeap[0];
const last = minHeap.pop();
if (minHeap.length > 0) {
minHeap[0] = last;
let i = 0;
while (true) {
let left = 2 * i + 1, right = 2 * i + 2, smallest = i;
if (left < minHeap.length && minHeap[left].val < minHeap[smallest].val) smallest = left;
if (right < minHeap.length && minHeap[right].val < minHeap[smallest].val) smallest = right;
if (smallest === i) break;
[minHeap[i], minHeap[smallest]] = [minHeap[smallest], minHeap[i]];
i = smallest;
}
}
return top;
}
let totalLen = 0;
for (let i = 0; i < arrays.length; i++) {
totalLen += arrays[i].length;
}
for (let i = 0; i < arrays.length; i++) {
if (arrays[i].length > 0) {
heapPush(new Element(arrays[i][0], i, 0));
}
}
if (totalLen === 0) throw new Error('No elements to find median');
const mid1 = Math.floor((totalLen - 1) / 2);
const mid2 = Math.floor(totalLen / 2);
let count = -1;
let median1 = 0, median2 = 0;
while (minHeap.length > 0) {
const e = heapPop();
count++;
if (count === mid1) median1 = e.val;
if (count === mid2) {
median2 = e.val;
break;
}
if (e.eleIdx + 1 < arrays[e.arrIdx].length) {
heapPush(new Element(arrays[e.arrIdx][e.eleIdx + 1], e.arrIdx, e.eleIdx + 1));
}
}
if (totalLen % 2 === 1) return median2;
else return (median1 + median2) / 2;
}
// Driver code
const arrays = [[1,3,5],[2,4,6],[7,8,9]];
console.log(findMedianKSortedArrays(arrays)); // Output: 5
Line Notes
class ElementClass to hold value and indices for heap ordering
let totalLen = 0;Initialize total length counter
for (let i = 0; i < arrays.length; i++)Iterate over each array to push first elements
if (arrays[i].length > 0)Only add if array is non-empty
heapPush(new Element(arrays[i][0], i, 0));Push first element of each array
const mid1 = Math.floor((totalLen - 1) / 2);Calculate first median index
const mid2 = Math.floor(totalLen / 2);Calculate second median index
while (minHeap.length > 0)Extract elements until median found
const e = heapPop();Pop smallest element
count++;Count elements popped
if (count === mid1) median1 = e.val;Record first median candidate
if (count === mid2)Record second median candidate and break
if (e.eleIdx + 1 < arrays[e.arrIdx].length)If next element exists, push it
heapPush(new Element(arrays[e.arrIdx][e.eleIdx + 1], e.arrIdx, e.eleIdx + 1));Push next element from same array
TimeO(N log K) where N is total elements, K is number of arrays
SpaceO(K) for the heap
Each element is pushed and popped once from the heap of size K, so operations cost O(log K).
💡 For example, with 1,000,000 elements and 100 arrays, this is roughly 1,000,000 * log(100) ≈ 6,600,000 operations, much faster than brute force.
Interview Verdict: Accepted / Efficient for large inputs
This approach is practical and commonly accepted in interviews for this problem.