🧠
Min-Heap with Pair Expansion and Visited Set
💡 This approach improves efficiency by using a min-heap to explore sums in ascending order without generating all pairs, teaching how to prune search space smartly.
Intuition
Start with the smallest pair sum (nums1[0] + nums2[0]) in a min-heap. Repeatedly extract the smallest sum and push the next possible pairs by incrementing indices, avoiding duplicates with a visited set.
Algorithm
- Initialize a min-heap and a visited set.
- Push the pair (0,0) with sum nums1[0] + nums2[0] into the heap and mark visited.
- Repeat k times: pop the smallest sum from the heap.
- For the popped pair (i,j), push pairs (i+1,j) and (i,j+1) if not visited.
- After k pops, the last popped sum is the answer.
💡 The heap ensures we always pick the next smallest sum, and the visited set prevents revisiting pairs, making the search efficient.
import heapq
def kthSmallestSum(nums1, nums2, k):
n, m = len(nums1), len(nums2)
min_heap = []
visited = set()
heapq.heappush(min_heap, (nums1[0] + nums2[0], 0, 0))
visited.add((0, 0))
for _ in range(k):
sum_val, i, j = heapq.heappop(min_heap)
if i + 1 < n and (i + 1, j) not in visited:
heapq.heappush(min_heap, (nums1[i + 1] + nums2[j], i + 1, j))
visited.add((i + 1, j))
if j + 1 < m and (i, j + 1) not in visited:
heapq.heappush(min_heap, (nums1[i] + nums2[j + 1], i, j + 1))
visited.add((i, j + 1))
return sum_val
# Driver code
if __name__ == '__main__':
nums1 = [1,7,11]
nums2 = [2,4,6]
k = 3
print(kthSmallestSum(nums1, nums2, k)) # Output: 7
Line Notes
import heapqImport heapq for efficient min-heap operations
min_heap = []Initialize min-heap to store sums with indices
visited = set()Track visited pairs to avoid duplicates
heapq.heappush(min_heap, (nums1[0] + nums2[0], 0, 0))Push initial smallest sum pair
for _ in range(k)Extract k times to find k-th smallest sum
heapq.heappop(min_heap)Pop smallest sum from heap
if i + 1 < n and (i + 1, j) not in visitedPush next pair by incrementing i if unvisited
if j + 1 < m and (i, j + 1) not in visitedPush next pair by incrementing j if unvisited
return sum_valReturn the k-th smallest sum after k pops
import java.util.*;
public class KthSmallestSum {
static class Pair implements Comparable<Pair> {
int sum, i, j;
Pair(int sum, int i, int j) {
this.sum = sum;
this.i = i;
this.j = j;
}
public int compareTo(Pair other) {
return this.sum - other.sum;
}
}
public static int kthSmallestSum(int[] nums1, int[] nums2, int k) {
int n = nums1.length, m = nums2.length;
PriorityQueue<Pair> minHeap = new PriorityQueue<>();
Set<String> visited = new HashSet<>();
minHeap.offer(new Pair(nums1[0] + nums2[0], 0, 0));
visited.add("0,0");
int sumVal = 0;
for (int count = 0; count < k; count++) {
Pair curr = minHeap.poll();
sumVal = curr.sum;
int i = curr.i, j = curr.j;
if (i + 1 < n && !visited.contains((i + 1) + "," + j)) {
minHeap.offer(new Pair(nums1[i + 1] + nums2[j], i + 1, j));
visited.add((i + 1) + "," + j);
}
if (j + 1 < m && !visited.contains(i + "," + (j + 1))) {
minHeap.offer(new Pair(nums1[i] + nums2[j + 1], i, j + 1));
visited.add(i + "," + (j + 1));
}
}
return sumVal;
}
public static void main(String[] args) {
int[] nums1 = {1,7,11};
int[] nums2 = {2,4,6};
int k = 3;
System.out.println(kthSmallestSum(nums1, nums2, k)); // Output: 7
}
}
Line Notes
static class Pair implements Comparable<Pair>Define Pair class to store sum and indices with ordering
PriorityQueue<Pair> minHeap = new PriorityQueue<>()Min-heap to get smallest sum pairs
Set<String> visited = new HashSet<>()Track visited pairs as strings to avoid duplicates
minHeap.offer(new Pair(nums1[0] + nums2[0], 0, 0))Add initial smallest sum pair
for (int count = 0; count < k; count++)Extract k times to find k-th smallest sum
Pair curr = minHeap.poll()Pop smallest sum pair
if (i + 1 < n && !visited.contains(...))Add next pair by incrementing i if unvisited
if (j + 1 < m && !visited.contains(...))Add next pair by incrementing j if unvisited
return sumValReturn the k-th smallest sum after k pops
#include <iostream>
#include <vector>
#include <queue>
#include <set>
using namespace std;
struct Pair {
int sum, i, j;
Pair(int s, int x, int y) : sum(s), i(x), j(y) {}
bool operator>(const Pair& other) const {
return sum > other.sum;
}
};
int kthSmallestSum(vector<int>& nums1, vector<int>& nums2, int k) {
int n = nums1.size(), m = nums2.size();
priority_queue<Pair, vector<Pair>, greater<Pair>> minHeap;
set<pair<int,int>> visited;
minHeap.emplace(nums1[0] + nums2[0], 0, 0);
visited.insert({0, 0});
int sumVal = 0;
for (int count = 0; count < k; count++) {
Pair curr = minHeap.top();
minHeap.pop();
sumVal = curr.sum;
int i = curr.i, j = curr.j;
if (i + 1 < n && visited.find({i + 1, j}) == visited.end()) {
minHeap.emplace(nums1[i + 1] + nums2[j], i + 1, j);
visited.insert({i + 1, j});
}
if (j + 1 < m && visited.find({i, j + 1}) == visited.end()) {
minHeap.emplace(nums1[i] + nums2[j + 1], i, j + 1);
visited.insert({i, j + 1});
}
}
return sumVal;
}
int main() {
vector<int> nums1 = {1,7,11};
vector<int> nums2 = {2,4,6};
int k = 3;
cout << kthSmallestSum(nums1, nums2, k) << endl; // Output: 7
return 0;
}
Line Notes
struct PairStruct to hold sum and indices with comparison operator
priority_queue<Pair, vector<Pair>, greater<Pair>> minHeapMin-heap for smallest sums
set<pair<int,int>> visitedSet to track visited pairs
minHeap.emplace(nums1[0] + nums2[0], 0, 0)Push initial smallest sum pair
for (int count = 0; count < k; count++)Extract k times to find k-th smallest sum
Pair curr = minHeap.top(); minHeap.pop();Pop smallest sum pair
if (i + 1 < n && visited.find(...) == visited.end())Add next pair by incrementing i if unvisited
if (j + 1 < m && visited.find(...) == visited.end())Add next pair by incrementing j if unvisited
return sumValReturn the k-th smallest sum
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 kthSmallestSum(nums1, nums2, k) {
const n = nums1.length, m = nums2.length;
const minHeap = new MinHeap();
const visited = new Set();
minHeap.push([nums1[0] + nums2[0], 0, 0]);
visited.add('0,0');
let sumVal = 0;
for (let count = 0; count < k; count++) {
const [sum, i, j] = minHeap.pop();
sumVal = sum;
if (i + 1 < n && !visited.has(`${i + 1},${j}`)) {
minHeap.push([nums1[i + 1] + nums2[j], i + 1, j]);
visited.add(`${i + 1},${j}`);
}
if (j + 1 < m && !visited.has(`${i},${j + 1}`)) {
minHeap.push([nums1[i] + nums2[j + 1], i, j + 1]);
visited.add(`${i},${j + 1}`);
}
}
return sumVal;
}
// Test
const nums1 = [1,7,11];
const nums2 = [2,4,6];
const k = 3;
console.log(kthSmallestSum(nums1, nums2, k)); // Output: 7
Line Notes
class MinHeapCustom min-heap implementation for priority queue
minHeap.push([nums1[0] + nums2[0], 0, 0])Push initial smallest sum pair
const visited = new Set()Track visited pairs to avoid duplicates
for (let count = 0; count < k; count++)Extract k times to find k-th smallest sum
const [sum, i, j] = minHeap.pop()Pop smallest sum pair
if (i + 1 < n && !visited.has(...))Add next pair by incrementing i if unvisited
if (j + 1 < m && !visited.has(...))Add next pair by incrementing j if unvisited
return sumValReturn the k-th smallest sum
We push at most k pairs into the heap, each heap operation costs O(log k). Visited set prevents duplicates.
💡 For k=1000, this means roughly 1000 log 1000 ≈ 10,000 operations, which is efficient for large arrays.
Interview Verdict: Accepted
This approach is optimal and accepted for large inputs, demonstrating efficient use of heaps and pruning.