1855. Maximum Distance Between a Pair of Values
Problem Description
You are given two integer arrays nums1
and nums2
that are both non-increasing (sorted in descending order or staying the same).
Your task is to find a pair of indices (i, j)
where:
i
is an index fromnums1
(where0 <= i < nums1.length
)j
is an index fromnums2
(where0 <= j < nums2.length
)
For a pair (i, j)
to be valid, it must satisfy two conditions:
i <= j
(the index innums1
cannot be greater than the index innums2
)nums1[i] <= nums2[j]
(the value at indexi
innums1
must be less than or equal to the value at indexj
innums2
)
The distance of a valid pair (i, j)
is calculated as j - i
.
You need to find the maximum distance among all valid pairs. If no valid pairs exist, return 0
.
Example of non-increasing array: An array like [5, 4, 4, 2, 1]
is non-increasing because each element is less than or equal to the previous one.
The solution uses binary search to efficiently find valid pairs. For each element in nums1
at index i
, it searches for the rightmost position in nums2
(starting from index i
) where a valid pair can be formed, maximizing the distance j - i
.
Intuition
Since both arrays are non-increasing (sorted in descending order), we can leverage this property to efficiently find valid pairs.
For each element nums1[i]
, we want to find the farthest valid index j
in nums2
such that nums1[i] <= nums2[j]
and i <= j
. To maximize the distance j - i
, we need to find the largest possible j
.
Because nums2
is non-increasing, once we find a position where nums1[i] > nums2[j]
, all positions after j
will also fail the condition nums1[i] <= nums2[j]
. This monotonic property makes binary search a perfect fit.
The key insight is that for each nums1[i]
, we're looking for the last (rightmost) position in nums2
where the value is still greater than or equal to nums1[i]
. Since nums2
is in descending order, all valid positions form a continuous range from the start of the search range up to some point.
To use binary search effectively with bisect_left
, the solution cleverly reverses nums2
. After reversing, nums2
becomes non-decreasing (ascending), which allows us to use bisect_left
to find where nums1[i]
would fit. The position found corresponds to the first element that is greater than or equal to nums1[i]
in the reversed array, which translates back to the last valid position in the original array.
By iterating through each element in nums1
and finding the optimal pairing in nums2
using binary search, we achieve an efficient O(m * log n)
solution instead of the brute force O(m * n)
approach.
Learn more about Two Pointers and Binary Search patterns.
Solution Approach
The solution uses binary search to efficiently find the maximum distance between valid pairs.
Step-by-step Implementation:
-
Initialize the answer: Start with
ans = 0
to track the maximum distance found. -
Reverse nums2: The solution reverses
nums2
usingnums2[::-1]
. This transforms the non-increasing array into a non-decreasing array, making it suitable for using Python'sbisect_left
function.- Original
nums2
:[5, 4, 2, 1]
(non-increasing) - Reversed
nums2
:[1, 2, 4, 5]
(non-decreasing)
- Original
-
Iterate through nums1: For each element
nums1[i]
with valuev
: -
Binary search in reversed nums2: Use
bisect_left(nums2, v)
on the reversed array to find the leftmost position wherev
could be inserted while maintaining sorted order.bisect_left
returns the index wherev
should be inserted in the reversed array- This corresponds to the first element that is greater than or equal to
v
in the reversed array
-
Convert back to original index: Calculate the actual index
j
in the originalnums2
:j = len(nums2) - bisect_left(nums2, v) - 1
- Since the array was reversed, we need to map the position back to the original array
- This gives us the last position in the original
nums2
wherenums2[j] >= nums1[i]
-
Calculate distance and update maximum:
- Compute the distance as
j - i
- Update
ans
with the maximum of currentans
and the new distance - Note: The solution doesn't explicitly check
i <= j
because negative distances will never be the maximum
- Compute the distance as
-
Return the result: After checking all elements in
nums1
, return the maximum distance found.
Time Complexity: O(m * log n)
where m
is the length of nums1
and n
is the length of nums2
. We perform a binary search (O(log n)
) for each element in nums1
.
Space Complexity: O(n)
for creating the reversed copy of nums2
.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to understand how the solution works.
Given:
nums1 = [10, 8, 2]
(non-increasing)nums2 = [12, 11, 8, 5, 2]
(non-increasing)
Step 1: Initialize and Reverse
ans = 0
- Reverse
nums2
to get[2, 5, 8, 11, 12]
(now non-decreasing)
Step 2: Process each element in nums1
For i = 0, nums1[0] = 10:
- Use
bisect_left([2, 5, 8, 11, 12], 10)
→ returns index 3- This means 10 would be inserted at position 3 in the reversed array
- Convert back to original index:
j = 5 - 3 - 1 = 1
- In original
nums2
, position 1 has value 11, and indeed10 ≤ 11
✓ - Distance =
1 - 0 = 1
- Update
ans = max(0, 1) = 1
For i = 1, nums1[1] = 8:
- Use
bisect_left([2, 5, 8, 11, 12], 8)
→ returns index 2- Value 8 exists, so it returns the position of the first 8
- Convert back to original index:
j = 5 - 2 - 1 = 2
- In original
nums2
, position 2 has value 8, and indeed8 ≤ 8
✓ - Distance =
2 - 1 = 1
- Update
ans = max(1, 1) = 1
For i = 2, nums1[2] = 2:
- Use
bisect_left([2, 5, 8, 11, 12], 2)
→ returns index 0- Value 2 exists at the beginning of reversed array
- Convert back to original index:
j = 5 - 0 - 1 = 4
- In original
nums2
, position 4 has value 2, and indeed2 ≤ 2
✓ - Distance =
4 - 2 = 2
- Update
ans = max(1, 2) = 2
Result: The maximum distance is 2
, achieved by the pair (i=2, j=4)
where nums1[2] = 2
and nums2[4] = 2
.
The binary search efficiently finds the rightmost valid position in nums2
for each element in nums1
, avoiding the need to check every possible pair.
Solution Implementation
1from typing import List
2from bisect import bisect_left
3
4
5class Solution:
6 def maxDistance(self, nums1: List[int], nums2: List[int]) -> int:
7 """
8 Find the maximum distance j - i where i <= j and nums1[i] <= nums2[j].
9
10 Args:
11 nums1: First list of integers (non-increasing order)
12 nums2: Second list of integers (non-increasing order)
13
14 Returns:
15 Maximum distance between valid index pairs
16 """
17 max_distance = 0
18
19 # Reverse nums2 to convert from non-increasing to non-decreasing
20 # This allows us to use binary search effectively
21 reversed_nums2 = nums2[::-1]
22
23 # Iterate through each element in nums1 with its index
24 for i, value in enumerate(nums1):
25 # Find the leftmost position in reversed_nums2 where we can insert 'value'
26 # This gives us the count of elements >= value in reversed array
27 position_in_reversed = bisect_left(reversed_nums2, value)
28
29 # Convert back to original index in nums2
30 # The rightmost valid j in original nums2 where nums2[j] >= value
31 j = len(nums2) - position_in_reversed - 1
32
33 # Update maximum distance if current distance is larger
34 max_distance = max(max_distance, j - i)
35
36 return max_distance
37
1class Solution {
2 /**
3 * Finds the maximum distance between two indices i and j where:
4 * - i <= j
5 * - nums1[i] <= nums2[j]
6 * - distance = j - i
7 *
8 * @param nums1 First non-increasing array
9 * @param nums2 Second non-increasing array
10 * @return Maximum distance between valid pairs
11 */
12 public int maxDistance(int[] nums1, int[] nums2) {
13 int maxDistance = 0;
14 int nums1Length = nums1.length;
15 int nums2Length = nums2.length;
16
17 // Iterate through each element in nums1 as the starting point
18 for (int i = 0; i < nums1Length; i++) {
19 // Binary search to find the rightmost index j in nums2
20 // where nums2[j] >= nums1[i]
21 int left = i; // Start from index i to ensure i <= j
22 int right = nums2Length - 1;
23
24 // Binary search for the rightmost valid position
25 while (left < right) {
26 // Calculate mid with ceiling division to bias towards right
27 int mid = (left + right + 1) >> 1;
28
29 if (nums2[mid] >= nums1[i]) {
30 // Valid pair found, try to find a larger index
31 left = mid;
32 } else {
33 // nums2[mid] < nums1[i], search in left half
34 right = mid - 1;
35 }
36 }
37
38 // Update maximum distance if current distance is larger
39 maxDistance = Math.max(maxDistance, left - i);
40 }
41
42 return maxDistance;
43 }
44}
45
1class Solution {
2public:
3 int maxDistance(vector<int>& nums1, vector<int>& nums2) {
4 int maxDist = 0;
5
6 // Reverse nums2 to enable binary search for values >= target
7 // After reversal, nums2 becomes sorted in ascending order
8 reverse(nums2.begin(), nums2.end());
9
10 // For each element in nums1, find the farthest valid index in nums2
11 for (int i = 0; i < nums1.size(); ++i) {
12 // Use binary search to find the leftmost position in reversed nums2
13 // where value >= nums1[i]
14 auto it = lower_bound(nums2.begin(), nums2.end(), nums1[i]);
15
16 // Convert the iterator position back to original index in nums2
17 // Since nums2 was reversed, the original index is:
18 // (size - position_in_reversed - 1)
19 int j = nums2.size() - (it - nums2.begin()) - 1;
20
21 // Update maximum distance if current distance (j - i) is larger
22 maxDist = max(maxDist, j - i);
23 }
24
25 return maxDist;
26 }
27};
28
1/**
2 * Finds the maximum distance between valid index pairs (i, j) where:
3 * - i <= j
4 * - nums1[i] <= nums2[j]
5 * The distance is defined as j - i
6 *
7 * @param nums1 - First non-increasing array
8 * @param nums2 - Second non-increasing array
9 * @returns The maximum distance between valid pairs
10 */
11function maxDistance(nums1: number[], nums2: number[]): number {
12 let maxDistanceFound: number = 0;
13 const nums1Length: number = nums1.length;
14 const nums2Length: number = nums2.length;
15
16 // Iterate through each element in nums1 as the starting index i
17 for (let i = 0; i < nums1Length; ++i) {
18 // Binary search to find the rightmost index j in nums2
19 // where nums2[j] >= nums1[i]
20 let leftBound: number = i; // Start search from index i (since i <= j)
21 let rightBound: number = nums2Length - 1;
22
23 // Binary search for the largest valid j
24 while (leftBound < rightBound) {
25 // Calculate mid point, using +1 to bias towards right when even length
26 const midPoint: number = (leftBound + rightBound + 1) >> 1;
27
28 if (nums2[midPoint] >= nums1[i]) {
29 // nums2[midPoint] satisfies the condition, search right half
30 leftBound = midPoint;
31 } else {
32 // nums2[midPoint] is too small, search left half
33 rightBound = midPoint - 1;
34 }
35 }
36
37 // Update maximum distance if current distance is larger
38 maxDistanceFound = Math.max(maxDistanceFound, leftBound - i);
39 }
40
41 return maxDistanceFound;
42}
43
Time and Space Complexity
The time complexity is O(m × log n)
, where m
is the length of nums1
and n
is the length of nums2
. This is because:
- The code iterates through each element in
nums1
(which takesO(m)
time) - For each element, it performs a binary search using
bisect_left
on the reversednums2
array (which takesO(log n)
time per search) - Therefore, the total time complexity is
O(m × log n)
The space complexity is O(n)
, not O(1)
as stated in the reference answer. This is because:
- The line
nums2 = nums2[::-1]
creates a reversed copy ofnums2
, which requiresO(n)
additional space - The other variables (
ans
,i
,v
,j
) only use constant space
Note: The reference answer's space complexity of O(1)
would be correct if the code modified nums2
in-place or used a different approach that didn't create a copy of the array.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly handling the index mapping after reversal
When reversing nums2
and using binary search, a common mistake is incorrectly converting the position back to the original index. The formula j = len(nums2) - position_in_reversed - 1
can be confusing.
Why this happens: After reversing, bisect_left
returns the position where the value would be inserted in the reversed array. This position needs to be mapped back to find the rightmost valid index in the original array.
Example of the mistake:
# Wrong: forgetting the -1
j = len(nums2) - position_in_reversed # This gives an off-by-one error
# Wrong: using bisect_right instead
position = bisect_right(reversed_nums2, value)
j = len(nums2) - position - 1 # This may miss valid pairs
Solution: Always remember that for a reversed array of length n, index i in the reversed array corresponds to index (n-1-i) in the original array.
2. Not checking if j >= i before calculating distance
The current solution calculates j - i
for all cases and relies on max()
to filter out negative distances. However, this can be inefficient and unclear.
Better approach:
for i, value in enumerate(nums1):
position_in_reversed = bisect_left(reversed_nums2, value)
j = len(nums2) - position_in_reversed - 1
# Only update if j >= i (valid pair condition)
if j >= i:
max_distance = max(max_distance, j - i)
3. Misunderstanding bisect_left behavior with duplicates
When nums2
contains duplicate values, bisect_left
finds the leftmost position in the reversed array. This correctly translates to the rightmost position in the original array, maximizing the distance.
Example scenario:
- Original
nums2 = [5, 3, 3, 3, 1]
- Reversed
nums2 = [1, 3, 3, 3, 5]
- Searching for value 3:
bisect_left
returns index 1 - This maps to index 3 in the original array (the rightmost 3)
Pitfall: Using bisect_right
instead would give a different position and potentially miss the maximum distance.
4. Forgetting edge cases
The solution doesn't explicitly handle edge cases like empty arrays or when all pairs are invalid.
Improved version with edge case handling:
def maxDistance(self, nums1: List[int], nums2: List[int]) -> int:
if not nums1 or not nums2:
return 0
max_distance = 0
reversed_nums2 = nums2[::-1]
for i, value in enumerate(nums1):
# Early termination: if i is already beyond nums2's length
if i >= len(nums2):
break
position_in_reversed = bisect_left(reversed_nums2, value)
j = len(nums2) - position_in_reversed - 1
if j >= i:
max_distance = max(max_distance, j - i)
return max_distance
How does quick sort divide the problem into subproblems?
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!