LeetCode 2040 - Kth Smallest Product of Two Sorted Arrays
The problem asks for the k-th smallest product that can be formed by multiplying one element from the sorted array nums1 with one element from the sorted array nums2. Both arrays may contain negative numbers, zeros, and positive numbers.
Difficulty: 🔴 Hard
Topics: Array, Binary Search
Solution
Problem Understanding
The problem asks for the k-th smallest product that can be formed by multiplying one element from the sorted array nums1 with one element from the sorted array nums2. Both arrays may contain negative numbers, zeros, and positive numbers. The value of k is 1-based, meaning k = 1 corresponds to the smallest product overall. The arrays are sorted, but they may have different lengths, and the total number of products can be as large as nums1.length * nums2.length, which can be up to $2.5 \times 10^9$ given the constraints.
Key points to note include the presence of negative numbers, which reverse the order of products (multiplying two negatives produces a positive), and zeros, which can dominate the k-th smallest product if k is small. Naively computing all possible products and sorting them will exceed time and space limits because the product array could reach billions of entries. Therefore, a more efficient approach that leverages the sorted nature of the arrays is required.
Important edge cases include arrays with all negative numbers, arrays containing zero, or arrays with mixed signs where the k-th product might come from multiplying a negative with a positive.
Approaches
The brute-force approach would involve generating all possible products from nums1[i] * nums2[j], storing them in a list, sorting the list, and returning the k-th element. While correct, this approach has time and space complexity proportional to the product of the lengths of the arrays, which is infeasible for large inputs.
The optimal approach uses binary search on the value of the product rather than the indices. The key insight is that for a fixed candidate product x, we can efficiently count how many products are less than or equal to x using a two-pointer or binary search strategy on the second array for each element in the first array. This count lets us adjust the search range to home in on the k-th smallest product without generating all products.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n * m log(n * m)) | O(n * m) | Generate all products, sort, and pick k-th |
| Optimal | O((n + m) * log(max_val - min_val)) | O(1) | Binary search on product values with counting using two-pointer/binary search |
Algorithm Walkthrough
- Define search boundaries: The smallest possible product is
min(nums1) * min(nums2)ormin(nums1) * max(nums2), and the largest ismax(nums1) * max(nums2)ormax(nums1) * min(nums2)depending on signs. Use these as initial binary search bounds. - Binary search on product values: Repeat until the search interval converges. For each candidate
midvalue, count how many products are less than or equal tomid. - Counting products <= mid:
For each number a in nums1:
- If
ais positive, count elements innums2such thata * b <= midusing binary search (sincenums2is sorted). - If
ais negative, count elements innums2such thata * b <= mid, which translates to counting from the other end ofnums2. - If
ais zero andmid >= 0, allnums2elements contribute to the count.
- Adjust search interval: If the count of products <=
midis less thank, setlow = mid + 1; otherwise, sethigh = mid. Repeat. - Return the final result: Once
lowequalshigh, it represents the k-th smallest product.
Why it works: The invariant is that the binary search maintains the range where the k-th smallest product must lie. The counting function guarantees that if there are at least k products less than or equal to a candidate, the true k-th product cannot be smaller than this candidate. This ensures convergence to the correct answer.
Python Solution
from bisect import bisect_right, bisect_left
from typing import List
class Solution:
def kthSmallestProduct(self, nums1: List[int], nums2: List[int], k: int) -> int:
def count_leq(x: int) -> int:
count = 0
for a in nums1:
if a > 0:
count += bisect_right(nums2, x // a)
elif a < 0:
count += len(nums2) - bisect_left(nums2, (x + (-a-1)) // a) # careful integer division
else:
if x >= 0:
count += len(nums2)
return count
left, right = -10**10, 10**10
while left < right:
mid = (left + right) // 2
if count_leq(mid) < k:
left = mid + 1
else:
right = mid
return left
Implementation walkthrough: The count_leq function efficiently counts products <= x using binary search on nums2. Positive a values use bisect_right, negative a values use bisect_left with a transformation to handle negative products, and zeros contribute when x >= 0. The binary search over product values ensures the k-th smallest product is found without generating all products.
Go Solution
import "sort"
func kthSmallestProduct(nums1 []int, nums2 []int, k int64) int64 {
countLEQ := func(x int64) int64 {
var count int64 = 0
for _, a := range nums1 {
if a > 0 {
idx := sort.Search(len(nums2), func(i int) bool {
return int64(nums2[i])*int64(a) > x
})
count += int64(idx)
} else if a < 0 {
idx := sort.Search(len(nums2), func(i int) bool {
return int64(nums2[i])*int64(a) > x
})
count += int64(len(nums2) - idx)
} else {
if x >= 0 {
count += int64(len(nums2))
}
}
}
return count
}
left, right := int64(-1e10), int64(1e10)
for left < right {
mid := (left + right) / 2
if countLEQ(mid) < k {
left = mid + 1
} else {
right = mid
}
}
return left
}
Go-specific notes: Integer overflow is handled by converting products to int64. The sort.Search function replicates binary search functionality, similar to Python's bisect. Slices are used for nums2 without copying, keeping memory usage minimal.
Worked Examples
Example 1
nums1 = [2,5], nums2 = [3,4], k = 2
| Step | mid | count_leq(mid) | Adjusted Search |
|---|---|---|---|
| 1 | 0 | 0 | left = 1 |
| 2 | ... | ... | ... |
| Final | 8 | 2 | converges |
Result: 8
Example 2
nums1 = [-4,-2,0,3], nums2 = [2,4], k = 6
The binary search counts negative, zero, and positive products separately, converging to 0.
Example 3
nums1 = [-2,-1,0,1,2], nums2 = [-3,-1,2,4,5], k = 3
Binary search finds -6 as the 3rd smallest product.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O((n + m) * log(max_val - min_val)) | Each binary search step counts products in O(n log m), repeated log(range) times |
| Space | O(1) | No additional storage except loop variables |
The complexity is efficient because the binary search drastically reduces the number of candidate product values to consider, and counting products leverages the sorted arrays with binary search.
Test Cases
# provided examples
assert Solution().kthSmallestProduct([2,5], [3,4], 2) == 8 # Example 1
assert Solution().kthSmallestProduct([-4,-2,0,3], [2,4], 6) == 0 # Example 2
assert Solution().kthSmallestProduct([-2,-1,0,1,2], [-3,-1,2,4,5], 3) == -6 # Example 3
# edge cases
assert Solution().kthSmallestProduct([0], [0], 1) == 0 # both zero
assert Solution().kthSmallestProduct([-1,-1,-1], [1,2,3], 1) == -3 # all negative * positive
assert Solution().kthSmallestProduct([1,2,3], [-1,-2,-3], 5) == -2 # mixed signs
assert Solution().kthSmallestProduct([1], [1