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]wherenis the length of the array - The indices
iandjcan 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
icreates 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 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] 371class 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} 391class 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}; 381/** 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} 45Solution 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, wheremitracks the index of the minimum value andmxtracks the index of the maximum value among all valid positions. -
Iterate from
indexDifferenceto the end: We start our loop at indexi = indexDifferencebecause any index before this wouldn't have a valid partner that'sindexDifferencepositions away. -
Calculate the valid partner index: For each position
i, we calculatej = i - indexDifference. Thisjrepresents 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
indexDifferenceaway 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.
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.
What's the output of running the following function using input 56?
1KEYBOARD = { 2 '2': 'abc', 3 '3': 'def', 4 '4': 'ghi', 5 '5': 'jkl', 6 '6': 'mno', 7 '7': 'pqrs', 8 '8': 'tuv', 9 '9': 'wxyz', 10} 11 12def letter_combinations_of_phone_number(digits): 13 def dfs(path, res): 14 if len(path) == len(digits): 15 res.append(''.join(path)) 16 return 17 18 next_number = digits[len(path)] 19 for letter in KEYBOARD[next_number]: 20 path.append(letter) 21 dfs(path, res) 22 path.pop() 23 24 res = [] 25 dfs([], res) 26 return res 271private static final Map<Character, char[]> KEYBOARD = Map.of( 2 '2', "abc".toCharArray(), 3 '3', "def".toCharArray(), 4 '4', "ghi".toCharArray(), 5 '5', "jkl".toCharArray(), 6 '6', "mno".toCharArray(), 7 '7', "pqrs".toCharArray(), 8 '8', "tuv".toCharArray(), 9 '9', "wxyz".toCharArray() 10); 11 12public static List<String> letterCombinationsOfPhoneNumber(String digits) { 13 List<String> res = new ArrayList<>(); 14 dfs(new StringBuilder(), res, digits.toCharArray()); 15 return res; 16} 17 18private static void dfs(StringBuilder path, List<String> res, char[] digits) { 19 if (path.length() == digits.length) { 20 res.add(path.toString()); 21 return; 22 } 23 char next_digit = digits[path.length()]; 24 for (char letter : KEYBOARD.get(next_digit)) { 25 path.append(letter); 26 dfs(path, res, digits); 27 path.deleteCharAt(path.length() - 1); 28 } 29} 301const KEYBOARD = { 2 '2': 'abc', 3 '3': 'def', 4 '4': 'ghi', 5 '5': 'jkl', 6 '6': 'mno', 7 '7': 'pqrs', 8 '8': 'tuv', 9 '9': 'wxyz', 10} 11 12function letter_combinations_of_phone_number(digits) { 13 let res = []; 14 dfs(digits, [], res); 15 return res; 16} 17 18function dfs(digits, path, res) { 19 if (path.length === digits.length) { 20 res.push(path.join('')); 21 return; 22 } 23 let next_number = digits.charAt(path.length); 24 for (let letter of KEYBOARD[next_number]) { 25 path.push(letter); 26 dfs(digits, path, res); 27 path.pop(); 28 } 29} 30Recommended 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 If you prefer videos here's a video that explains recursion in a fun and easy way 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 him
Want a Structured Path to Master System Design Too? Don’t Miss This!