HardArraySliding WindowSortingBucket SortOrdered SetUpdated Sep 2026

220. Contains Duplicate III

Asked at Netflix

Difficulty

Hard

Time

O(n)

Space

O(min(n, indexDiff))

Pattern

Array

Companies

1

Problem

You get an integer array nums and two integers indexDiff and valueDiff. Your job is to decide if there is a pair of different positions i and j that passes all three tests at once:

  1. i != j — they are two different positions.
  2. abs(i - j) <= indexDiff — the positions are close together in the array.
  3. abs(nums[i] - nums[j]) <= valueDiff — the values at those positions are close in size.

Return true if at least one such pair exists, otherwise return false. Note that valueDiff can be 0, which means the two values must be exactly equal. The two positions must be distinct, so a single element can never pair with itself.

Two windows must overlap

numsdistance 3 <= indexDiff12310123Positions 0 and 3 are 3 apart, which fits indexDiff =3.values: 1 and 1|1 - 1| = 0 <= valueDiff = 0both rules pass -> true
We need one pair that is close in position AND close in value at the same time.

Constraints

  • ●2 <= nums.length <= 10^5
  • ●-10^9 <= nums[i] <= 10^9
  • ●1 <= indexDiff <= nums.length
  • ●0 <= valueDiff <= 10^9

Examples

Example 1
Inputnums = [1,2,3,1], indexDiff = 3, valueDiff = 0
Outputtrue
WhyTake i = 0 and j = 3. They are different positions, their distance is 3 which is <= indexDiff = 3, and both values are 1 so the value difference is 0 <= valueDiff = 0. All three rules hold.
Example 2
Inputnums = [1,5,9,1,5,9], indexDiff = 2, valueDiff = 3
Outputfalse
WhyEvery pair of equal values (1 and 1, 5 and 5, 9 and 9) sits 3 positions apart, which is more than indexDiff = 2. Pairs that are close in position always differ by at least 4 in value, which is more than valueDiff = 3. So no pair works.

Asked At

1 companies
CompanyDifficulty
NetflixHardView all Netflix questions →

How to Think About It

1

Brute force: check all pairs (i,j) with |i-j| <= indexDiff and |nums[i]-nums[j]| <= valueDiff. Time O(n * indexDiff) which can be O(n^2) in the worst case.

2

Key insight: bucket sort (address book). Numbers within valueDiff of each other fall into the same or adjacent buckets. Bucket width = valueDiff + 1. For each number, check the same bucket and neighboring buckets.

3

Why valueDiff + 1 as bucket width: if two numbers differ by at most valueDiff, they must be in the same bucket (when bucket width = valueDiff+1) or adjacent buckets. This guarantees we only check 3 buckets per number.

4

Sliding window: maintain at most indexDiff elements in the bucket map. When i > indexDiff, remove nums[i - indexDiff] from its bucket. This ensures |i-j| <= indexDiff.

5

Edge cases: valueDiff=0 means exact duplicate within indexDiff. indexDiff=0 means no valid pairs. Empty array returns false.

Optimal Approach

Step 1: Set bucket width = valueDiff + 1.
Step 2: For each index i, compute bucket = nums[i] // width.
Step 3: Check if the same bucket already has a number (guaranteed within valueDiff).
Step 4: Check if the left neighbor bucket has a number with nums[i] - left <= valueDiff.
Step 5: Check if the right neighbor bucket has a number with right - nums[i] <= valueDiff.
Step 6: If any match, return true. Otherwise, add nums[i] to its bucket.
Step 7: When i >= indexDiff, remove nums[i - indexDiff] from its bucket to maintain the window.
Step 8: If no match found after the loop, return false.

Walkthrough for nums=[1,5,9,1,5,9], indexDiff=2, valueDiff=3:

  • width=4. i=0, num=1, bucket=0: empty. Add.
  • i=1, num=5, bucket=1: check buckets 0,1,2. Bucket 0 has 1. 5-1=4 > 3. No match. Add.
  • i=2, num=9, bucket=2: check 1,2,3. Bucket 1 has 5. 9-5=4 > 3. No match. Add.
  • i=3, num=1, bucket=0: bucket 0 has old 1 (index 0, dist=3 > 2). Remove index 0 first. Bucket 0 now empty. Add index 3.
  • No match. Return false.

Time: O(n) - each element is inserted and removed from buckets at most once. Space: O(min(n, indexDiff)) for the bucket map.

Buckets of width valueDiff + 1

buckets (width = 1)012bucket1i = 0, value 1 -> bucket 0. No neighbor holds a value,so we store it.buckets (width = 1)012bucket123Values 2 and 3 fill buckets 1 and 2. Each check of thesame and neighbor buckets finds no match yet.buckets (width = 1)012bucket123i = 3, value 1 -> bucket 0 already holds 1, so thevalue difference is 0. Return true.bucket = x // (valueDiff + 1)check bucket, bucket-1, bucket+1drop nums[i - indexDiff] when i >= indexDiff
Each value goes into bucket value // (valueDiff + 1). Close values land in the same or a neighboring bucket.
ComplexityTime O(n)Space O(min(n, indexDiff))Each element is added to a bucket once and removed once, and only 3 buckets are checked per element; the map holds at most indexDiff elements.

What Trips People Up in Real Interviews

1

Using bucket width = valueDiff instead of valueDiff + 1. With width = valueDiff, two numbers that differ by exactly valueDiff might land in different non-adjacent buckets.

2

Forgetting to remove old elements from the bucket map. Without removal, the window is unbounded and you lose the |i-j| <= indexDiff constraint.

3

Using sorted containers instead of bucket sort. A TreeMap works but bucket sort is simpler and O(n) average. Both are acceptable.

4

Checking more than 3 buckets (same, left, right). With bucket width = valueDiff+1, numbers in the same or adjacent bucket are the only candidates. No need to check further.

5

Off-by-one in the sliding window removal. Remove nums[i - indexDiff] when i >= indexDiff, not i > indexDiff.

Solution Code

O(n) time
def containsNearbyAlmostDuplicate(nums, indexDiff, valueDiff):
    if indexDiff <= 0 or valueDiff < 0:
        return False
    buckets = {}
    width = valueDiff + 1
    for i, x in enumerate(nums):
        b = x // width
        if b in buckets:
            return True
        if b - 1 in buckets and x - buckets[b - 1] <= valueDiff:
            return True
        if b + 1 in buckets and buckets[b + 1] - x <= valueDiff:
            return True
        buckets[b] = x
        if i >= indexDiff:
            del buckets[nums[i - indexDiff] // width]
    return False

Pro at DSA?

Test your skills with a real FAANG-style mock interview.

Start a Mock Interview →

Frequently Asked Questions

What is the Contains Duplicate III problem?

Given an integer array nums and two integers indexDiff and valueDiff, return true if there exist two distinct indices i and j such that |i-j| <= indexDiff and |nums[i]-nums[j]| <= valueDiff. This extends Contains Duplicate II by adding a value proximity constraint.

How do you solve Contains Duplicate III?

The optimal approach runs in O(n) time and O(min(n, indexDiff)) space. Scroll up to "Optimal Approach" for the step-by-step walkthrough, diagrams, and code in Python, C++ and Java.

What companies ask Contains Duplicate III?

Contains Duplicate III is asked at Netflix. It is a hard difficulty problem.

What are common mistakes on Contains Duplicate III?
  • Using bucket width = valueDiff instead of valueDiff + 1. With width = valueDiff, two numbers that differ by exactly valueDiff might land in different non-adjacent buckets.
  • Forgetting to remove old elements from the bucket map. Without removal, the window is unbounded and you lose the |i-j| <= indexDiff constraint.
  • Using sorted containers instead of bucket sort. A TreeMap works but bucket sort is simpler and `O(n)` average. Both are acceptable.
  • Checking more than 3 buckets (same, left, right). With bucket width = valueDiff+1, numbers in the same or adjacent bucket are the only candidates. No need to check further.
  • Off-by-one in the sliding window removal. Remove nums[i - indexDiff] when i >= indexDiff, not i > indexDiff.