Contains Duplicate III

Given an array of integers, find out whether there are two distinct indices i and j in the array such that the absolute difference between nums[i] and nums[j] is at most t and the absolute difference between i and j is at most k.

Example 1:

Input: nums = [1,2,3,1], k = 3, t = 0
Output: true

Example 2:

Input: nums = [1,0,1,1], k = 1, t = 2
Output: true

Example 3:

Input: nums = [1,5,9,1,5,9], k = 2, t = 3
Output: false
class Solution {
    // O(nlogn) Solution
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        if (nums == null || nums.length < 2 || k < 1)
            return false;

        TreeSet<Long> set = new TreeSet<>();
        for (int i = 0; i < nums.length; i++) {
            long currNumber = (long) nums[i];
            Long floor = set.floor(currNumber + t);
            Long ceil = set.ceiling(currNumber - t);
            // If there is a values with +-t range of our current number
            if ((floor != null && floor >= currNumber) || (ceil != null && ceil <= currNumber))
                return true;

            set.add(currNumber);
            // Maintaining a window size -> K
            if (i >= k)
                set.remove((long) nums[i - k]);
        }
        return false;
    }
}

// O(n) Solution
public class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        if (k < 1 || t < 0)
            return false;
        // Bucketing means we map a range of values to the a bucket.
        // So, as a rule of thumb, just make sure the size of the bucket is reasonable
        // such that elements having the same bucket is immediately considered
        // duplicates or duplicates must lie within adjacent buckets. So this actually
        // gives us a range of possible bucket size, i.e. t and t + 1. We just choose it
        // to be t and a bucket mapping to be num / t.
        Map<Long, Long> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            // Another complication is that negative ints are allowed. A simple num / t just
            // shrinks everything towards 0. Therefore, we can just reposition every element
            // to start from Integer.MIN_VALUE.
            long remappedNum = (long) nums[i] - Integer.MIN_VALUE;
            long bucket = remappedNum / ((long) t + 1);
            // Buckets of size -> T
            // Now the number (if present) must be present in current bucket OR
            // previous bucket OR the next bucket
            if (map.containsKey(bucket) || (map.containsKey(bucket - 1) && remappedNum - map.get(bucket - 1) <= t)
                    || (map.containsKey(bucket + 1) && map.get(bucket + 1) - remappedNum <= t))
                return true;
            // Maintaining a window size -> K
            if (map.entrySet().size() >= k) {
                long lastBucket = ((long) nums[i - k] - Integer.MIN_VALUE) / ((long) t + 1);
                map.remove(lastBucket);
            }
            map.put(bucket, remappedNum);
        }
        return false;
    }
}

Last updated