Skip to content

220. Contains Duplicate III 👍

Approach 1: Ordered Set

  • Time: $O(n\log k)$
  • Space: $O(k)$
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Solution {
 public:
  bool containsNearbyAlmostDuplicate(vector<int>& nums, int indexDiff,
                                     int valueDiff) {
    set<long> window;

    for (int i = 0; i < nums.size(); ++i) {
      if (const auto it =
              window.lower_bound(static_cast<long>(nums[i]) - valueDiff);
          it != window.cend() && *it - nums[i] <= valueDiff)
        return true;
      window.insert(nums[i]);
      if (i >= indexDiff)
        window.erase(nums[i - indexDiff]);
    }

    return false;
  }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
  public boolean containsNearbyAlmostDuplicate(int[] nums, int indexDiff, int valueDiff) {
    TreeSet<Long> set = new TreeSet<>();

    for (int i = 0; i < nums.length; ++i) {
      final long num = (long) nums[i];
      final Long ceiling = set.ceiling(num); // The smallest num >= nums[i]
      if (ceiling != null && ceiling - num <= valueDiff)
        return true;
      final Long floor = set.floor(num); // The largest num <= nums[i]
      if (floor != null && num - floor <= valueDiff)
        return true;
      set.add(num);
      if (i >= indexDiff)
        set.remove((long) nums[i - indexDiff]);
    }

    return false;
  }
}

Approach 2: Bucket

  • Time: $O(n)$
  • Space: $O((\max(nums) - \min(nums)) / t) \to O(k)$
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
 public:
  bool containsNearbyAlmostDuplicate(vector<int>& nums, int indexDiff,
                                     int valueDiff) {
    if (nums.empty() || indexDiff <= 0 || valueDiff < 0)
      return false;

    const long mn = ranges::min(nums);
    const long diff = valueDiff + 1L;  // In case that `valueDiff` equals 0.
    // Use long because the corner case INT_MAX - (-1) will overflow.
    unordered_map<long, long> bucket;

    for (int i = 0; i < nums.size(); ++i) {
      const long num = nums[i];
      const long key = getKey(num, mn, diff);
      if (bucket.contains(key))  // the current bucket
        return true;
      if (bucket.contains(key - 1) &&
          num - bucket[key - 1] < diff)  // the left adjacent bucket
        return true;
      if (bucket.contains(key + 1) &&
          bucket[key + 1] - num < diff)  // the right adjacent bucket
        return true;
      bucket[key] = num;
      if (i >= indexDiff)
        bucket.erase(getKey(nums[i - indexDiff], mn, diff));
    }

    return false;
  }

 private:
  int getKey(long num, long mn, long diff) {
    return (num - mn) / diff;
  }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
  public boolean containsNearbyAlmostDuplicate(int[] nums, int indexDiff, int valueDiff) {
    if (nums.length == 0 || indexDiff <= 0 || valueDiff < 0)
      return false;

    final long mn = Arrays.stream(nums).min().getAsInt();
    final long diff = (long) valueDiff + 1; // In case that `valueDiff` equals 0.
    // Use Long because of corner case Integer.MAX_VALUE - (-1) will overflow
    HashMap<Long, Long> bucket = new HashMap<>();

    for (int i = 0; i < nums.length; ++i) {
      final long num = (long) nums[i];
      final long key = getKey(nums[i], mn, diff);
      if (bucket.containsKey(key)) // the current bucket
        return true;
      if (bucket.containsKey(key - 1) &&
          num - bucket.get(key - 1) < diff) // the left adjacent bucket
        return true;
      if (bucket.containsKey(key + 1) &&
          bucket.get(key + 1) - num < diff) // the right adjacent bucket
        return true;
      bucket.put(key, num);
      if (i >= indexDiff)
        bucket.remove(getKey(nums[i - indexDiff], mn, diff));
    }

    return false;
  }

  private long getKey(int num, long mn, long diff) {
    return ((long) num - mn) / diff;
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution:
  def containsNearbyAlmostDuplicate(
      self,
      nums: list[int],
      indexDiff: int,
      valueDiff: int,
  ) -> bool:
    if not nums or indexDiff <= 0 or valueDiff < 0:
      return False

    mn = min(nums)
    diff = valueDiff + 1  # In case that `valueDiff` equals 0.
    bucket = {}

    def getKey(num: int) -> int:
      return (num - mn) // diff

    for i, num in enumerate(nums):
      key = getKey(num)
      if key in bucket:  # the current bucket
        return True
      # the left adjacent bucket
      if key - 1 in bucket and num - bucket[key - 1] < diff:
        return True
      # the right adjacent bucket
      if key + 1 in bucket and bucket[key + 1] - num < diff:
        return True
      bucket[key] = num
      if i >= indexDiff:
        del bucket[getKey(nums[i - indexDiff])]

    return False