The Kth Smallest Product of Two Sorted Arrays problem is a classic interview question that checks whether you can shift from generating pair products to searching the answer via counting. Many candidates try to enumerate and sort all n × m products or force a two-pointer merge because the arrays are sorted, but that becomes infeasible and often breaks once negatives and zeros enter the mix.
Problem statement
You’re given two sorted integer arrays, nums1 and nums2, and an integer k. Consider every possible pair (i, j) and the product nums1[i] * nums2[j]. Your task is to return the kth smallest product when all these products are ordered from smallest to largest (with k being 1-based).
The challenge is that there can be billions of pair products, and the presence of negative numbers and zeros means the ordering of products is not straightforward. A “generate and sort” approach becomes impossible, and even seemingly clever two-pointer ideas break because product order flips depending on signs.
Constraints:
- 1 ≤
nums1.length,nums2.length≤ $5 * 10^4$ - $−10^5$ ≤
nums1[i],nums2[j]≤ $10^5$ - 1 ≤ k ≤
nums1.length * nums2.length nums1andnums2are sorted.
Examples
| Input | Output | ||
| nums1 | nums2 | k | kth smallest product |
| [1, 2, 3] | [4, 5] | 4 | Products: [4, 5, 8, 10, 12, 15] 4th smallest product is 10 |
| [-3, -1, 2] | [-2, 0, 3] | 5 | Sorted starts: [-9, -4, -3, 0, 0, 0, …] 5th smallest product is 0 |
| [-5, -4, -2] | [-3, -1] | 2 | Products: [2, 4, 5, 6, 12, 15] 2nd smallest product is 4 |
| [-2, -2, 1] | [-3, 4] | 3 | Products: [-8, -8, -3, 4, 6, 6] 3rd smallest product is -3 |
| [2] | [-7, -3, 0, 6] | 2 | Products: [-14, -6, 0, 12] 2nd smallest product is -6 |
Why generating every pair is impractical
There are len(nums1) * len(nums2) products. With lengths up to $5∗10^4$, the pair count can reach 2.5 billion, which is far beyond what you can enumerate, store, or sort. Even just iterating through all products would time out, and sorting them would be even worse.
The core issue is repeated work: most candidate products are irrelevant once you know roughly where the kth value must lie, but brute force computes them all anyway.
Optimized approach using Binary search
We can’t list every product from the two arrays because that would take too long. Instead, we guess a product value and ask: how many pairs of numbers (one from the first array, one from the second) produce a product that is at most this guess? If that count is at least k, then the real k-th smallest product must be at or below our guess, so we try a smaller guess. If the count is less than k, the k-th smallest product must be bigger, so we try a larger guess. This works because as our guess increases, the number of valid pairs can only stay the same or go up, never down.
The main trick is counting how many pairs are at most a given guess efficiently. Keep the second array sorted. Then handle the first array one number at a time: for a fixed number from the first array, you want to know how many numbers in the sorted second array keep the product at most the guess. Because the second array is sorted, the “good” numbers will always sit together at one end (either mostly on the left or mostly on the right), so you can find the cut point using binary search instead of scanning the whole array. Add up these counts across all numbers in the first array to get the total number of valid pairs.
Even with negative numbers, this per-element binary-search counting still works because the valid values in the sorted second array always form one continuous block.
Solution steps
- Initialize
low = -10**10andhigh = 10**10as the minimum/maximum possible product values. - Implement
ceil_div(p, q)to computeceil(p / q)correctly for negative divisors. - Implement
count_leq(v)to count how many productsnums1[i] * nums2[j]are<= v:- For each
xinnums1:- If
x > 0, count how manyyinnums2satisfyy <= floor(v / x)usingbisect_right. - If
x < 0, count how manyyinnums2satisfyy >= ceil(v / x)usingbisect_left. - If
x == 0, addlen(nums2)ifv >= 0, otherwise add0.
- If
- For each
- Binary search while
low < high:- Let
mid = (low + high) // 2. - If
count_leq(mid) >= k, move left by settinghigh = mid; otherwise setlow = mid + 1.
- Let
- Return
low.
Let’s look at the following illustration to get a better understanding of the solution.
Python implementation
Let’s look at the code for the solution we just discussed.
Time complexity
Let m = len(nums1) and n = len(nums2). Each count query loops over nums1 and performs a binary search in nums2, which takes O(logn) time per element, so one counting pass is O(mlogn). The binary search over the answer range runs in a fixed number of iterations (about 60 at most for this integer range), so the total time is O(mlogn∗logR) where R is the product value range, and in practice it behaves like O(mlogn) times a small constant.
Space complexity
The algorithm uses O(1) extra space beyond the input arrays. It maintains a few integers and uses binary search operations without building any auxiliary pair lists.
Edge cases
- All zeros in one array
Example:nums1= [0, 0] ,nums2= [-5, 7] ,k= 3
Products are all0, so the answer is0. The solution countsnpairs for each0whenx >= 0. - Only negative values
Example:nums1= [-5, -1],nums2= [-4, -2],k= 1
Negative * negative becomes positive, and ordering can still be non-trivial. Counting via bounds remains correct because it never assumes product monotonicity globally. - Mixed negatives, zeros, and positives
Example:nums1= [-1, 0, 2] ,nums2= [-3, 4],k= 2
Multiple sign regimes coexist; per-acounting handles each case separately. - Very large k near the end
Example:k = len(nums1) * len(nums2)
You’re effectively asking for the maximum product. The binary search still converges becausecount_leq(x)is monotone inx.
Common pitfalls
- Assuming products are “mergeable” because arrays are sorted
Sorting inputs does not imply sorted pairwise products when negatives exist. - Using integer division incorrectly for negatives
Fora < 0, you must use a correctceil(x / a)bound; mixing floor and ceil will off-by-one the count and break the binary search. - Off-by-one in
kkis 1-based, but the binary search logic naturally targets “smallest x with count >= k”, which matches 1-based ranking—don’t shiftk. - Trying to clamp search bounds too tightly without proof
It’s tempting to use only endpoint products as min/max, but with negatives and zeros, safe bounds are easier and still efficient.