Level Up Your Coding Skills & Crack Interviews — Save up to 50% or more on Educative.io Today! Claim Discount

Arrow
Table of contents

2040. Kth Smallest Product of Two Sorted Arrays

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
  • nums1 and nums2 are sorted.

Examples

Input Output
nums1nums2kkth smallest product
[1, 2, 3][4, 5]4Products: [4, 5, 8, 10, 12, 15] 4th smallest product is 10
[-3, -1, 2][-2, 0, 3]5Sorted starts: [-9, -4, -3, 0, 0, 0, …] 5th smallest product is 0
[-5, -4, -2][-3, -1]2Products: [2, 4, 5, 6, 12, 15] 2nd smallest product is 4
[-2, -2, 1][-3, 4]3Products: [-8, -8, -3, 4, 6, 6] 3rd smallest product is -3
[2][-7, -3, 0, 6]2Products: [-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.

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**10 and high = 10**10 as the minimum/maximum possible product values.
  • Implement ceil_div(p, q) to compute ceil(p / q) correctly for negative divisors.
  • Implement count_leq(v) to count how many products nums1[i] * nums2[j] are <= v:
    • For each x in nums1:
      • If x > 0, count how many y in nums2 satisfy y <= floor(v / x) using bisect_right.
      • If x < 0, count how many y in nums2 satisfy y >= ceil(v / x) using bisect_left.
      • If x == 0, add len(nums2) if v >= 0, otherwise add 0.
  • Binary search while low < high:
    • Let mid = (low + high) // 2.
    • If count_leq(mid) >= k, move left by setting high = mid; otherwise set low = mid + 1.
  • Return low.

Given a sorted list a and a value x:

  • bisect_left(a, x) → the index where you can insert x so that x goes before any existing equal values
  • bisect_right(a, x) → the index where you can insert x so that x goes after any existing equal values

Let’s look at the following illustration to get a better understanding of the solution.

1 / 6

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 all 0, so the answer is 0. The solution counts n pairs for each 0 when x >= 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-a counting 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 because count_leq(x) is monotone in x.

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
    For a < 0, you must use a correct ceil(x / a) bound; mixing floor and ceil will off-by-one the count and break the binary search.
  • Off-by-one in k
    k is 1-based, but the binary search logic naturally targets “smallest x with count >= k”, which matches 1-based ranking—don’t shift k.
  • 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.

Frequently Asked Questions

Why can’t I generate all products and sort them?

Because there can be up to 2.5∗10^9 products, which is far too many to compute or store.

How do I recognize that “binary search on the answer” is appropriate here?

When the output is a numeric value and you can efficiently count how many candidates are <= x, you can often binary search the value space.

Why does the counting function need to treat negative and positive nums1[i] differently?

Because multiplying by a negative reverses the ordering with respect to nums2[j]. The valid set of j indices becomes a suffix rather than a prefix.

Is there a faster-than-O(m log n) counting method?

In some variants, you can use two pointers when all values are non-negative (or otherwise constrained). With mixed signs, the binary-search-per-element approach stays simpler and reliably correct.

What changes if arrays weren’t sorted?

The efficient counting step would no longer work. You’d likely need to sort first (if allowed) or use a different approach entirely.

Share with others:

Leave a Reply

Your email address will not be published. Required fields are marked *