Prerequisites
Before attempting this problem, you should be comfortable with:
- Binary Search - Using binary search to find a target value or boundary in a sorted space
- Two Pointers Technique - Efficiently traversing sorted arrays from both ends to count elements meeting certain conditions
- Sorted Array Properties - Leveraging the sorted nature of arrays to optimize counting and searching operations
- Handling Negative Numbers - Understanding how multiplication sign rules affect product ordering
1. Brute Force
Intuition
The most direct approach computes all possible products by multiplying each element in the first array with each element in the second. After generating all products, we sort them and return the k-th smallest. While simple, this requires O(m * n) space and time for generation, plus O(m * n * log(m * n)) for sorting, making it impractical for large inputs.
Algorithm
- Create an empty list to store all products.
- For each element in
nums1, multiply it with each element innums2and add to the list. - Sort the product list.
- Return the element at index
k - 1.
Time & Space Complexity
- Time complexity:
- Space complexity:
Where and are the lengths of the arrays and , respectively.
2. Binary Search
Intuition
Instead of enumerating all products, we can binary search on the answer. For a candidate product value, we count how many products are less than or equal to it. If the count is less than k, we need a larger product; otherwise, we search smaller. The counting function leverages the sorted nature of both arrays: for positive numbers in nums1, we use upper bound on nums2; for negative numbers, we use lower bound with adjusted logic.
Algorithm
- Set the search range from
-10^10to10^10(product bounds). - Binary search for the smallest product where at least
kproducts are less than or equal to it. - For counting products <=
prod:- For positive
ainnums1: count elements innums2wherea * b <= prod. - For negative
a: count elements where the product doesn't exceedprod(direction reverses). - For
a = 0: ifprod >= 0, all ofnums2contributes.
- For positive
- Return the binary search result.
class Solution:
def kthSmallestProduct(self, nums1: List[int], nums2: List[int], k: int) -> int:
def count(prod):
cnt = 0
n2 = len(nums2)
for a in nums1:
if a > 0:
cnt += bisect.bisect_right(nums2, prod // a)
elif a < 0:
threshold = math.ceil(prod / a)
idx = bisect.bisect_left(nums2, threshold)
cnt += n2 - idx
else:
if prod >= 0:
cnt += n2
return cnt
l, r = -(10**10), 10**10
while l <= r:
m = l + (r - l) // 2
if count(m) < k:
l = m + 1
else:
r = m - 1
return lTime & Space Complexity
- Time complexity:
- Space complexity:
Where and are the lengths of the arrays and , respectively. is the size of the range of the product.
3. Binary Search + Two Pointers
Intuition
We can optimize the counting step using two pointers instead of binary search for each element. By separating negative and non-negative numbers in both arrays, we handle four cases: negative times negative (positive result), positive times positive, negative times positive, and positive times negative. For each case, two pointers can efficiently count products less than or equal to the target by exploiting monotonicity.
Algorithm
- Find the boundary indices where negative numbers end in both arrays (
pos1,pos2). - Binary search on the product value.
- For each candidate, count products using four two-pointer traversals:
- Negatives from
nums1with negatives fromnums2(yields positives). - Positives from
nums1with positives fromnums2. - Negatives from
nums1with positives fromnums2(yields negatives). - Positives from
nums1with negatives fromnums2.
- Negatives from
- Adjust pointers based on whether the current product exceeds the target.
- Return the smallest product where count >=
k.
class Solution:
def kthSmallestProduct(self, nums1: List[int], nums2: List[int], k: int) -> int:
n1, n2 = len(nums1), len(nums2)
pos1 = 0 # first non-negative in nums1
while pos1 < n1 and nums1[pos1] < 0:
pos1 += 1
pos2 = 0 # first non-negative in nums2
while pos2 < n2 and nums2[pos2] < 0:
pos2 += 1
def count(prod):
cnt = 0
# negative * negative -> positive
i, j = 0, pos2 - 1
while i < pos1 and j >= 0:
if nums1[i] * nums2[j] > prod:
i += 1
else:
cnt += (pos1 - i)
j -= 1
# positive * positive -> positive
i, j = pos1, n2 - 1
while i < n1 and j >= pos2:
if nums1[i] * nums2[j] > prod:
j -= 1
else:
cnt += (j - pos2 + 1)
i += 1
# negative * positive -> negative
i, j = 0, pos2
while i < pos1 and j < n2:
if nums1[i] * nums2[j] > prod:
j += 1
else:
cnt += (n2 - j)
i += 1
# positive * negative → negative
i, j = pos1, 0
while i < n1 and j < pos2:
if nums1[i] * nums2[j] > prod:
i += 1
else:
cnt += (n1 - i)
j += 1
return cnt
left, right = -10**10, 10**10
while left <= right:
mid = (left + right) // 2
if count(mid) < k:
left = mid + 1
else:
right = mid - 1
return leftTime & Space Complexity
- Time complexity:
- Space complexity:
Where and are the lengths of the arrays and , respectively. is the size of the range of the product.
Common Pitfalls
Integer Overflow in Product Calculations
Products of two integers can exceed the 32-bit integer range, especially when values approach 10^5 in magnitude. Always use 64-bit integers (long long in C++, long in Java) for product calculations and comparisons. Failing to do so causes incorrect counts and wrong answers.
Incorrect Handling of Negative Numbers
When one or both arrays contain negative numbers, the sign of the product flips, and the ordering reverses. A negative number multiplied by a large positive yields a more negative (smaller) result. You must handle negative-negative, negative-positive, positive-negative, and positive-positive cases separately to count products correctly.
Incorrect Floor/Ceiling Division for Negative Values
Standard integer division truncates toward zero, but counting products requires floor division for positive divisors and ceiling logic for negative divisors. Using the wrong rounding direction causes off-by-one errors in binary search bounds. Use language-specific floor division functions or manually adjust for negative operands.
Not Accounting for Zero Values
When an element is zero, its product with any number is zero. This affects counting: if the target product is non-negative, all pairs involving zero contribute to the count. Forgetting to handle zeros separately leads to missing or double-counting products.
Binary Search Boundary Errors
The search range must cover all possible products, from the most negative to the most positive. Setting bounds too tight causes the algorithm to miss the answer. Use safe bounds like -10^10 to 10^10 based on the problem constraints, and ensure the binary search condition correctly finds the smallest value with at least k products less than or equal to it.