Facebook Pixel

2903. Find Indices With Index and Value Difference I

Problem Description

You are given an integer array nums (0-indexed) with length n, along with two integer parameters: indexDifference and valueDifference.

Your task is to find two indices i and j (both in the range [0, n - 1]) that satisfy both of these conditions:

  1. The absolute difference between the indices must be at least indexDifference: abs(i - j) >= indexDifference
  2. The absolute difference between the values at these indices must be at least valueDifference: abs(nums[i] - nums[j]) >= valueDifference

The function should return an array answer where:

  • answer = [i, j] if such a pair of indices exists
  • answer = [-1, -1] if no such pair exists

Key points to note:

  • The indices i and j can be equal (when both indexDifference and valueDifference are 0)
  • If multiple valid pairs exist, you can return any one of them
  • The order matters in the return value - you need to return exactly [i, j] as found

The solution uses a sliding window approach with two pointers maintaining a gap of indexDifference. As the window slides through the array, it tracks the minimum and maximum values seen in the left portion of the window. For each position i, it checks if the current element forms a valid pair with either the minimum or maximum element from the valid range (indices that are at least indexDifference away). This ensures an efficient O(n) time complexity solution.

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 where abs(i - j) >= indexDifference and abs(nums[i] - nums[j]) >= valueDifference.

Let's think about this systematically. For a given index i, all valid j indices must be at least indexDifference away from i. This means we're looking at indices in the range [0, i - indexDifference] when i >= indexDifference.

Now, for the value difference condition abs(nums[i] - nums[j]) >= valueDifference, we need to maximize the absolute difference. The absolute difference is maximized when we pair nums[i] with either:

  1. The smallest value in the valid range (to maximize nums[i] - nums[j] when nums[i] is large)
  2. The largest value in the valid range (to maximize nums[j] - nums[i] when nums[i] is small)

This leads us to the core strategy: as we iterate through the array with index i, we only need to track two things from the valid range [0, i - indexDifference]:

  • The index of the minimum value (mi)
  • The index of the maximum value (mx)

Why does this work? Because if there exists any valid pair involving index i, it must involve either the minimum or maximum value from the valid range. Any other value would give us a smaller absolute difference, so checking just these two extremes is sufficient.

The sliding window naturally emerges from this insight. As i moves forward, the valid range grows by including one more element at position i - indexDifference. We update our tracked minimum and maximum indices if this new element is smaller or larger than our current extremes, then check if either extreme forms a valid pair with nums[i].

This reduces our problem from checking all possible pairs (O(nΒ²)) to just checking two specific pairs at each position (O(n)).

Learn more about Two Pointers patterns.

Solution Approach

The implementation uses a two-pointer technique combined with tracking minimum and maximum values to achieve an O(n) time complexity solution.

Initialize tracking variables:

  • mi = mx = 0: These variables store the indices of the minimum and maximum values in the valid range. Initially both point to index 0.

Main iteration: Starting from i = indexDifference to len(nums) - 1:

  1. Calculate the left boundary: j = i - indexDifference

    • This represents the newest element that enters our valid range (elements at least indexDifference away from i)
  2. Update minimum and maximum indices:

    • If nums[j] < nums[mi], update mi = j (found a new minimum in the valid range)
    • If nums[j] > nums[mx], update mx = j (found a new maximum in the valid range)

    Note that we use strict inequalities here. This ensures we only update when we find strictly better values, maintaining stability in our choices.

  3. Check for valid pairs:

    • First check: nums[i] - nums[mi] >= valueDifference
      • This checks if the current element minus the minimum value meets the threshold
      • If true, return [mi, i]
    • Second check: nums[mx] - nums[i] >= valueDifference
      • This checks if the maximum value minus the current element meets the threshold
      • If true, return [mx, i]

Why this order of checks works:

  • By checking nums[i] - nums[mi] first, we handle cases where nums[i] is relatively large
  • By checking nums[mx] - nums[i] second, we handle cases where nums[i] is relatively small
  • Together, these two checks cover all possible ways to achieve abs(nums[i] - nums[j]) >= valueDifference

Return value: If the loop completes without finding a valid pair, return [-1, -1].

Time and Space Complexity:

  • Time: O(n) - single pass through the array
  • Space: O(1) - only using a constant amount of extra variables

The elegance of this solution lies in recognizing that we only need to track extremes (minimum and maximum) from the valid range, rather than considering all elements, which transforms a potentially quadratic problem into a linear one.

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 the algorithm with a concrete example:

Input: nums = [5, 1, 4, 1, 9], indexDifference = 2, valueDifference = 4

We need to find indices i and j where:

  • abs(i - j) >= 2 (indices are at least 2 apart)
  • abs(nums[i] - nums[j]) >= 4 (values differ by at least 4)

Initial Setup:

  • mi = 0, mx = 0 (both tracking index 0 initially, where nums[0] = 5)

Iteration 1: i = 2

  • Calculate left boundary: j = 2 - 2 = 0
  • Check if nums[0] = 5 updates our min/max:
    • Is 5 < nums[mi] = 5? No, so mi stays 0
    • Is 5 > nums[mx] = 5? No, so mx stays 0
  • Check for valid pairs with nums[2] = 4:
    • nums[2] - nums[mi] = 4 - 5 = -1. Is -1 >= 4? No
    • nums[mx] - nums[2] = 5 - 4 = 1. Is 1 >= 4? No
  • No valid pair found, continue

Iteration 2: i = 3

  • Calculate left boundary: j = 3 - 2 = 1
  • Check if nums[1] = 1 updates our min/max:
    • Is 1 < nums[mi] = 5? Yes! Update mi = 1
    • Is 1 > nums[mx] = 5? No, so mx stays 0
  • Now mi = 1 (value 1), mx = 0 (value 5)
  • Check for valid pairs with nums[3] = 1:
    • nums[3] - nums[mi] = 1 - 1 = 0. Is 0 >= 4? No
    • nums[mx] - nums[3] = 5 - 1 = 4. Is 4 >= 4? Yes!
  • Found valid pair: return [0, 3]

Verification:

  • Index difference: abs(0 - 3) = 3 >= 2 βœ“
  • Value difference: abs(nums[0] - nums[3]) = abs(5 - 1) = 4 >= 4 βœ“

The algorithm correctly identifies [0, 3] as a valid answer. Note how tracking just the minimum and maximum indices from the valid range was sufficient - we didn't need to check all possible pairs, just the extremes against the current position.

Solution Implementation

1class Solution:
2    def findIndices(
3        self, nums: List[int], indexDifference: int, valueDifference: int
4    ) -> List[int]:
5        # Track indices of minimum and maximum values seen so far
6        min_index = 0
7        max_index = 0
8      
9        # Start from indexDifference position to ensure index difference constraint
10        for i in range(indexDifference, len(nums)):
11            # Calculate the corresponding left boundary index
12            j = i - indexDifference
13          
14            # Update minimum value index if we found a smaller value at position j
15            if nums[j] < nums[min_index]:
16                min_index = j
17          
18            # Update maximum value index if we found a larger value at position j
19            if nums[j] > nums[max_index]:
20                max_index = j
21          
22            # Check if current element minus minimum meets value difference requirement
23            if nums[i] - nums[min_index] >= valueDifference:
24                return [min_index, i]
25          
26            # Check if maximum minus current element meets value difference requirement
27            if nums[max_index] - nums[i] >= valueDifference:
28                return [max_index, i]
29      
30        # No valid pair found
31        return [-1, -1]
32
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 index that maintains required index difference
10            int j = i - indexDifference;
11          
12            // Update minimum value index if current element at j is smaller
13            if (nums[j] < nums[minIndex]) {
14                minIndex = j;
15            }
16          
17            // Update maximum value index if current element at j is larger
18            if (nums[j] > nums[maxIndex]) {
19                maxIndex = j;
20            }
21          
22            // Check if difference between current element and minimum meets value requirement
23            if (nums[i] - nums[minIndex] >= valueDifference) {
24                return new int[] {minIndex, i};
25            }
26          
27            // Check if difference between maximum and current element meets value requirement
28            if (nums[maxIndex] - nums[i] >= valueDifference) {
29                return new int[] {maxIndex, i};
30            }
31        }
32      
33        // Return [-1, -1] if no valid pair of indices found
34        return new int[] {-1, -1};
35    }
36}
37
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 constraint is met
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 the value difference requirement
24            if (nums[i] - nums[minIndex] >= valueDifference) {
25                return {minIndex, i};
26            }
27          
28            // Check if maximum minus current element meets the value difference requirement
29            if (nums[maxIndex] - nums[i] >= valueDifference) {
30                return {maxIndex, i};
31            }
32        }
33      
34        // Return {-1, -1} if no valid pair is found
35        return {-1, -1};
36    }
37};
38
1/**
2 * Finds two indices i and j in the array such that:
3 * - The absolute difference between indices is at least indexDifference
4 * - The absolute difference between values is at least valueDifference
5 * 
6 * @param nums - The input array of numbers
7 * @param indexDifference - Minimum required difference between indices
8 * @param valueDifference - Minimum required difference between values
9 * @returns Array containing [i, j] if found, otherwise [-1, -1]
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 array starting from indexDifference position
17    for (let currentIndex: number = indexDifference; currentIndex < nums.length; currentIndex++) {
18        // Calculate the corresponding index that maintains minimum index difference
19        const previousIndex: number = currentIndex - indexDifference;
20      
21        // Update minimum index if we found a smaller value
22        if (nums[previousIndex] < nums[minIndex]) {
23            minIndex = previousIndex;
24        }
25      
26        // Update maximum index if we found a larger value
27        if (nums[previousIndex] > nums[maxIndex]) {
28            maxIndex = previousIndex;
29        }
30      
31        // Check if current value minus minimum value meets value difference requirement
32        if (nums[currentIndex] - nums[minIndex] >= valueDifference) {
33            return [minIndex, currentIndex];
34        }
35      
36        // Check if maximum value minus current value meets value difference 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 array nums.

The algorithm uses a single for loop that iterates from index indexDifference to len(nums) - 1. This means it performs at most n - indexDifference iterations. Inside each iteration, all operations are constant time O(1):

  • Calculating j = i - indexDifference is O(1)
  • Comparing and updating mi and mx variables is O(1)
  • Checking the two conditions for value difference is O(1)

Since the loop runs at most n times and each iteration takes constant time, the overall time complexity is O(n).

Space Complexity: O(1)

The algorithm only uses a fixed amount of extra space regardless of the input size:

  • Two integer variables mi and mx to track indices of minimum and maximum values
  • Loop variable i and calculated variable j

No additional data structures that scale with input size are used, making the space complexity constant O(1).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrectly Handling the Index Difference Constraint

A common mistake is misunderstanding how the sliding window maintains the index difference. Developers might incorrectly think they need to check all elements in the range [0, i - indexDifference] instead of just updating with the single element at position i - indexDifference.

Incorrect approach:

# Wrong: Checking all elements up to i - indexDifference
for i in range(indexDifference, len(nums)):
    for j in range(0, i - indexDifference + 1):  # This makes it O(nΒ²)
        if abs(nums[i] - nums[j]) >= valueDifference:
            return [j, i]

Correct approach: The solution maintains running min/max indices, only updating them with the newest valid element at position j = i - indexDifference.

2. Using Non-Strict Inequalities When Updating Min/Max

Using <= or >= when updating min/max indices can lead to unnecessary updates and potential inefficiency in certain edge cases.

Problematic:

if nums[j] <= nums[min_index]:  # Using <= instead of <
    min_index = j

Why it matters: When values are equal, we should keep the earlier index as it's already been compared against more elements. Using strict inequality < ensures we only update when we find a strictly better value.

3. Forgetting to Check Both Directions for Value Difference

A critical mistake is only checking one direction of the absolute value difference, missing valid pairs.

Incorrect:

# Only checking if current element is larger than minimum
if nums[i] - nums[min_index] >= valueDifference:
    return [min_index, i]
# Missing the check for when current element is smaller than maximum!

Correct: The solution must check both:

  • nums[i] - nums[min_index] >= valueDifference (when nums[i] is large)
  • nums[max_index] - nums[i] >= valueDifference (when nums[i] is small)

4. Initializing Min/Max Indices Incorrectly

Starting with invalid indices or extreme values instead of actual array indices.

Wrong:

min_index = -1  # Invalid index
max_index = -1
# Or using value-based initialization
min_val = float('inf')
max_val = float('-inf')

Correct: Initialize both min_index and max_index to 0, as they represent actual valid indices in the array.

5. Off-by-One Error in Loop Range

Starting the loop at the wrong position or using incorrect boundary conditions.

Common mistakes:

# Starting too early (doesn't ensure index difference)
for i in range(len(nums)):
    j = i - indexDifference
    if j < 0:  # Need extra check
        continue

# Or starting too late
for i in range(indexDifference + 1, len(nums)):  # Misses valid index at indexDifference

Correct: Start exactly at indexDifference to ensure the first valid j is 0, and continue to len(nums) - 1.

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

Which of the following shows the order of node visit in a Breadth-first Search?


Recommended Readings

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

Load More