Facebook Pixel

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:

  1. The distance between the indices must be at least indexDifference: abs(i - j) >= indexDifference
  2. 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] where n is the length of the array
  • The indices i and j 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 (when nums[i] is larger), or
  • nums[j] - nums[i] >= valueDifference (when nums[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:

  1. Update our running minimum and maximum from the position that just became valid (i - indexDifference)
  2. Check if the current value at i creates a sufficient difference with either the tracked minimum or maximum
  3. 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:

  1. Initialize tracking variables: We start with mi = mx = 0, where mi tracks the index of the minimum value and mx tracks the index of the maximum value among all valid positions.

  2. Iterate from indexDifference to the end: We start our loop at index i = indexDifference because any index before this wouldn't have a valid partner that's indexDifference positions away.

  3. Calculate the valid partner index: For each position i, we calculate j = i - indexDifference. This j represents the newest index that just became valid (satisfies the index difference requirement with position i).

  4. Update minimum and maximum trackers:

    • If nums[j] < nums[mi], we update mi = j (found a new minimum)
    • If nums[j] > nums[mx], we update mx = 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 from i.

  5. 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.
  6. 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 Evaluator

Example 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 (since indexDifference = 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, keep mi = 0
    • Is 3 > nums[mx]? β†’ 3 > 3? No, keep mx = 0
  • 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, keep mi = 0
    • Is 8 > nums[mx]? β†’ 8 > 3? Yes! Update mx = 1
  • 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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of these pictures shows the visit order of a depth-first search?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More