2905. Find Indices With Index and Value Difference II
Problem Description
You are given an array nums
of integers (0-indexed), and two parameters: indexDifference
and valueDifference
.
Your goal is to find two indices i
and j
from the array that satisfy both of these conditions:
- The distance between the indices must be at least
indexDifference
:abs(i - j) >= indexDifference
- The difference between the values at those indices must be at least
valueDifference
:abs(nums[i] - nums[j]) >= valueDifference
If such a pair of indices exists, return them as [i, j]
. If no such pair exists, return [-1, -1]
.
Key points:
- Both indices must be within the valid range
[0, n-1]
wheren
is the length of the array - The indices
i
andj
can be equal - If multiple valid pairs exist, you can return any of them
- The absolute value function ensures that the order of indices doesn't matter for the conditions
For example, if nums = [5, 1, 4, 1]
, indexDifference = 2
, and valueDifference = 4
, then indices 0
and 2
would be valid because:
abs(0 - 2) = 2 >= 2
(satisfies index difference)abs(5 - 1) = 4 >= 4
(satisfies value difference)
The solution uses a sliding window approach where it maintains the minimum and maximum values seen so far that are at least indexDifference
positions away from the current position, allowing for efficient checking of both conditions in a single pass through the array.
Intuition
The key insight is that for any index i
, we need to find an index j
that is at least indexDifference
positions away and has a value difference of at least valueDifference
.
Let's think about this systematically. For each position i
in the array, we want to check if there's a valid j
such that abs(i - j) >= indexDifference
. To satisfy the value difference condition abs(nums[i] - nums[j]) >= valueDifference
, we need either:
nums[i] - nums[j] >= valueDifference
(whennums[i]
is larger), ornums[j] - nums[i] >= valueDifference
(whennums[j]
is larger)
This means for any position i
, we want to find either the smallest or largest value among all valid j
positions, because:
- The smallest value maximizes
nums[i] - nums[j]
- The largest value maximizes
nums[j] - nums[i]
Instead of checking all possible pairs (which would be O(nΒ²)), we can be smarter. As we iterate through the array at position i
, we know that valid j
indices must satisfy j <= i - indexDifference
.
So the strategy becomes: maintain track of the minimum and maximum values (and their indices) among all positions that are at least indexDifference
steps behind our current position. As we move forward:
- Update our running minimum and maximum from the position that just became valid (
i - indexDifference
) - Check if the current value at
i
creates a sufficient difference with either the tracked minimum or maximum - If yes, we found our answer; if not, continue
This way, we only need one pass through the array, checking at each position whether we can form a valid pair with the best candidates (minimum and maximum) from the valid range behind us.
Learn more about Two Pointers patterns.
Solution Approach
The implementation uses a sliding window technique with two pointers to track the minimum and maximum values seen so far that are at least indexDifference
positions away.
Here's how the algorithm works step by step:
-
Initialize tracking variables: We start with
mi = mx = 0
, wheremi
tracks the index of the minimum value andmx
tracks the index of the maximum value among all valid positions. -
Iterate from
indexDifference
to the end: We start our loop at indexi = indexDifference
because any index before this wouldn't have a valid partner that'sindexDifference
positions away. -
Calculate the valid partner index: For each position
i
, we calculatej = i - indexDifference
. Thisj
represents the newest index that just became valid (satisfies the index difference requirement with positioni
). -
Update minimum and maximum trackers:
- If
nums[j] < nums[mi]
, we updatemi = j
(found a new minimum) - If
nums[j] > nums[mx]
, we updatemx = j
(found a new maximum)
This ensures we always have the indices of the smallest and largest values among all positions that are at least
indexDifference
away fromi
. - If
-
Check for valid pairs:
- First, check if
nums[i] - nums[mi] >= valueDifference
. If true, return[mi, i]
as we found a valid pair where the current value is sufficiently larger than the minimum. - Then, check if
nums[mx] - nums[i] >= valueDifference
. If true, return[mx, i]
as we found a valid pair where the maximum is sufficiently larger than the current value.
- First, check if
-
Return default if no pair found: If we complete the loop without finding a valid pair, return
[-1, -1]
.
The algorithm runs in O(n) time complexity with O(1) space complexity, as we only maintain two index pointers and iterate through the array once. Each element is examined exactly once when it becomes the current position i
and once when it becomes a valid partner at position j
.
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 an example with nums = [3, 8, 2, 9, 1]
, indexDifference = 2
, and valueDifference = 5
.
We need to find indices i
and j
where:
abs(i - j) >= 2
(indices are at least 2 positions apart)abs(nums[i] - nums[j]) >= 5
(values differ by at least 5)
Initial Setup:
mi = 0, mx = 0
(both tracking index 0 initially)- Start loop at
i = 2
(sinceindexDifference = 2
)
Iteration 1: i = 2
- Current value:
nums[2] = 2
- Calculate valid partner:
j = 2 - 2 = 0
- Update trackers with
nums[0] = 3
:- Is
3 < nums[mi]
? β3 < 3
? No, keepmi = 0
- Is
3 > nums[mx]
? β3 > 3
? No, keepmx = 0
- Is
- Check for valid pairs:
nums[2] - nums[mi] = 2 - 3 = -1
(not >= 5)nums[mx] - nums[2] = 3 - 2 = 1
(not >= 5)
- Continue...
Iteration 2: i = 3
- Current value:
nums[3] = 9
- Calculate valid partner:
j = 3 - 2 = 1
- Update trackers with
nums[1] = 8
:- Is
8 < nums[mi]
? β8 < 3
? No, keepmi = 0
- Is
8 > nums[mx]
? β8 > 3
? Yes! Updatemx = 1
- Is
- Check for valid pairs:
nums[3] - nums[mi] = 9 - 3 = 6
(>= 5) β- Found valid pair! Return
[0, 3]
Result: [0, 3]
is returned because:
- Index difference:
abs(0 - 3) = 3 >= 2
β - Value difference:
abs(3 - 9) = 6 >= 5
β
The algorithm efficiently found the answer by maintaining the minimum value seen so far (at index 0) and checking if the current value at index 3 creates a sufficient difference.
Solution Implementation
1class Solution:
2 def findIndices(
3 self, nums: List[int], indexDifference: int, valueDifference: int
4 ) -> List[int]:
5 # Initialize indices to track minimum and maximum elements
6 # among elements at least indexDifference positions before current position
7 min_index = 0
8 max_index = 0
9
10 # Start from indexDifference position to ensure we have enough distance
11 for current_index in range(indexDifference, len(nums)):
12 # Calculate the index that is exactly indexDifference positions before
13 previous_index = current_index - indexDifference
14
15 # Update minimum element tracker if we found a smaller element
16 if nums[previous_index] < nums[min_index]:
17 min_index = previous_index
18
19 # Update maximum element tracker if we found a larger element
20 if nums[previous_index] > nums[max_index]:
21 max_index = previous_index
22
23 # Check if current element and minimum element satisfy the value difference
24 # nums[current_index] - nums[min_index] >= valueDifference means
25 # the current element is sufficiently larger than the minimum
26 if nums[current_index] - nums[min_index] >= valueDifference:
27 return [min_index, current_index]
28
29 # Check if maximum element and current element satisfy the value difference
30 # nums[max_index] - nums[current_index] >= valueDifference means
31 # the maximum is sufficiently larger than the current element
32 if nums[max_index] - nums[current_index] >= valueDifference:
33 return [max_index, current_index]
34
35 # No valid pair found that satisfies both constraints
36 return [-1, -1]
37
1class Solution {
2 public int[] findIndices(int[] nums, int indexDifference, int valueDifference) {
3 // Track indices of minimum and maximum values seen so far
4 int minIndex = 0;
5 int maxIndex = 0;
6
7 // Iterate through array starting from indexDifference position
8 for (int i = indexDifference; i < nums.length; i++) {
9 // Calculate the corresponding left boundary index
10 int j = i - indexDifference;
11
12 // Update minimum index if we found a smaller value at position j
13 if (nums[j] < nums[minIndex]) {
14 minIndex = j;
15 }
16
17 // Update maximum index if we found a larger value at position j
18 if (nums[j] > nums[maxIndex]) {
19 maxIndex = j;
20 }
21
22 // Check if current element and minimum element satisfy the value difference
23 // nums[i] - nums[minIndex] >= valueDifference means the difference is large enough
24 if (nums[i] - nums[minIndex] >= valueDifference) {
25 return new int[] {minIndex, i};
26 }
27
28 // Check if maximum element and current element satisfy the value difference
29 // nums[maxIndex] - nums[i] >= valueDifference means the difference is large enough
30 if (nums[maxIndex] - nums[i] >= valueDifference) {
31 return new int[] {maxIndex, i};
32 }
33 }
34
35 // No valid pair found, return [-1, -1]
36 return new int[] {-1, -1};
37 }
38}
39
1class Solution {
2public:
3 vector<int> findIndices(vector<int>& nums, int indexDifference, int valueDifference) {
4 // Track indices of minimum and maximum values seen so far
5 int minIndex = 0;
6 int maxIndex = 0;
7
8 // Start from indexDifference position to ensure index difference constraint
9 for (int i = indexDifference; i < nums.size(); ++i) {
10 // Calculate the corresponding left boundary index
11 int j = i - indexDifference;
12
13 // Update minimum value index if we found a smaller value at position j
14 if (nums[j] < nums[minIndex]) {
15 minIndex = j;
16 }
17
18 // Update maximum value index if we found a larger value at position j
19 if (nums[j] > nums[maxIndex]) {
20 maxIndex = j;
21 }
22
23 // Check if current element minus minimum meets value difference requirement
24 if (nums[i] - nums[minIndex] >= valueDifference) {
25 return {minIndex, i};
26 }
27
28 // Check if maximum minus current element meets value difference requirement
29 if (nums[maxIndex] - nums[i] >= valueDifference) {
30 return {maxIndex, i};
31 }
32 }
33
34 // No valid pair found
35 return {-1, -1};
36 }
37};
38
1/**
2 * Finds two indices i and j such that:
3 * - |i - j| >= indexDifference
4 * - |nums[i] - nums[j]| >= valueDifference
5 *
6 * @param nums - The input array of numbers
7 * @param indexDifference - The minimum required difference between indices
8 * @param valueDifference - The minimum required difference between values
9 * @returns An array containing two valid indices [i, j], or [-1, -1] if no valid pair exists
10 */
11function findIndices(nums: number[], indexDifference: number, valueDifference: number): number[] {
12 // Track indices of minimum and maximum values seen so far
13 let minIndex: number = 0;
14 let maxIndex: number = 0;
15
16 // Iterate through the array starting from indexDifference position
17 for (let currentIndex: number = indexDifference; currentIndex < nums.length; currentIndex++) {
18 // Calculate the index that is exactly indexDifference positions behind current
19 const comparisonIndex: number = currentIndex - indexDifference;
20
21 // Update minimum index if we found a smaller value at comparisonIndex
22 if (nums[comparisonIndex] < nums[minIndex]) {
23 minIndex = comparisonIndex;
24 }
25
26 // Update maximum index if we found a larger value at comparisonIndex
27 if (nums[comparisonIndex] > nums[maxIndex]) {
28 maxIndex = comparisonIndex;
29 }
30
31 // Check if current value minus minimum value meets the valueDifference requirement
32 if (nums[currentIndex] - nums[minIndex] >= valueDifference) {
33 return [minIndex, currentIndex];
34 }
35
36 // Check if maximum value minus current value meets the valueDifference requirement
37 if (nums[maxIndex] - nums[currentIndex] >= valueDifference) {
38 return [maxIndex, currentIndex];
39 }
40 }
41
42 // No valid pair found
43 return [-1, -1];
44}
45
Time and Space Complexity
Time Complexity: O(n)
, where n
is the length of the input array nums
. The algorithm uses a single loop that iterates from index indexDifference
to len(nums) - 1
. In each iteration, it performs constant time operations: updating the minimum and maximum indices (mi
and mx
) and checking two conditions. Since each operation inside the loop takes O(1)
time and the loop runs at most n - indexDifference
times, the overall time complexity is O(n)
.
Space Complexity: O(1)
. The algorithm uses only a constant amount of extra space regardless of the input size. It maintains two integer variables mi
and mx
to track the indices of minimum and maximum values encountered so far, plus the loop variable i
and the calculated index j
. No additional data structures that scale with input size are used, resulting in constant space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Initialization of Min/Max Indices
A common mistake is initializing min_index
and max_index
to invalid values like -1
or float('inf')
. This causes issues when comparing nums[min_index]
or nums[max_index]
before any valid elements have been processed.
Wrong approach:
min_index = -1 # Invalid index! max_index = -1
Correct approach:
min_index = 0 max_index = 0
2. Updating Min/Max with Current Index Instead of Previous Index
Developers often mistakenly update the min/max trackers using the current index i
instead of the previous valid index j = i - indexDifference
.
Wrong approach:
if nums[current_index] < nums[min_index]: # Wrong! Using current_index min_index = current_index
Correct approach:
if nums[previous_index] < nums[min_index]: # Correct! Using previous_index min_index = previous_index
3. Starting Loop from Wrong Position
Starting the loop from index 0 instead of indexDifference
means the first few iterations won't have valid partner indices that satisfy the distance requirement.
Wrong approach:
for current_index in range(len(nums)): # Starts too early!
previous_index = current_index - indexDifference
# previous_index could be negative here
Correct approach:
for current_index in range(indexDifference, len(nums)):
previous_index = current_index - indexDifference
# previous_index is always >= 0
4. Using Absolute Value in Comparisons
Since we're tracking both minimum and maximum values, using abs()
in the comparison checks is redundant and can miss valid pairs.
Wrong approach:
if abs(nums[current_index] - nums[min_index]) >= valueDifference:
return [min_index, current_index]
Correct approach:
# Check both directions without abs() if nums[current_index] - nums[min_index] >= valueDifference: return [min_index, current_index] if nums[max_index] - nums[current_index] >= valueDifference: return [max_index, current_index]
5. Forgetting to Handle Edge Case When indexDifference = 0
When indexDifference = 0
, the same index can be used for both i
and j
. The algorithm handles this correctly by allowing previous_index = current_index
when both are the same, but developers might add unnecessary special case handling.
Unnecessary complication:
if indexDifference == 0:
# Special handling not needed!
for i in range(len(nums)):
if abs(nums[i] - nums[i]) >= valueDifference: # Always 0!
return [i, i]
The standard algorithm already handles this case correctly without special treatment.
Which of these pictures shows the visit order of a depth-first search?
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
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
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!