220. 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

1. brute force
注意里面有用到long,因为可能会有边界条件:
[-1,2147483647]
1
2147483647
而 2147483647 + 1 = -2147483648
class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        Map<Integer, HashSet<Integer>> map = new HashMap();
        int le = nums.length;
        for(int i = 0; i < le; i++){
            for(int j = i + 1; j < le; j++){
                long abs = Math.abs((long)nums[j] - (long)nums[i]);
                if(abs <= t){
                    if(j - i <= k){
                        System.out.println(abs);
                        return true;
                    }                    
                }                
            }
        }
        return false;
    }
}

2. 桶排序,具体看讲解

 public class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        if (k < 1 || t < 0) return false;
        Map<Long, Long> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            long remappedNum = (long) nums[i] - Integer.MIN_VALUE;
            long bucket = remappedNum / ((long) t + 1);
            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;
            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;
    }
}

大概意思是因为两数最大差是t,就把桶的容量设为t+1,例如[0,1,2,3,4], t = 2,桶0【0,1,2】,桶1【3,4】,而且可以避免t==0的以t为桶容量时候除以0

最开始要remap所有的数,避免负数情况以及Integer.MAX_VALUE == 2^31 - 1的情况,很狗,所以下面所有的数都变成long.

如果两数在一个桶里(通过map.containsKey()实现,说明两数之差小于t),但也要注意相邻桶的状况,比如2和3也只差1<t==2, 说明我们也要判断一下左边右边桶里的数。!!!看到这才发现,每只桶里一直最多只有1个元素(这样才能比较)

--》最后是if (map.entrySet().size() >= k) ,这个是因为两个index不能超过k,那为啥这么实现?因为我们index是i,从0到k-1一直相安无事,直到i > =k,就像一个window.然后因为我们是先判断后put,最终会维持map size为k

很巧妙的方法,但也用了很多时间读懂,还要提高

class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        
        if(k < 1 || t < 0)
            return false;
        
        TreeSet<Long> values = new TreeSet<>();
        for (int i = 0; i < nums.length; i++) {
            long num = nums[i];    // 转为 Long 型,避免了整形溢出情况。
            Long floor = values.floor(num + t); // 小于等于
            Long ceil = values.ceiling(num - t); // 大于等于

            if((floor != null && floor >= num) //确保floor不是滥竽充数,比如floor < num
                    || (ceil != null && ceil <= num)) //和上面想同
                return true;
            
            values.add(num);
            if(i >= k)
                values.remove((long)(nums[i - k]));
        }
        return false;
    }
}

https://leetcode.com/problems/contains-duplicate-iii/discuss/61655/Java-O(N-lg-K)-solution

用treeset,每次查一下有没有floor = n - t <= n <= ceil = n + t, 如果有floor或者ceil存在于这个size为k的treeset中,就返回true。

posted @ 2020-06-25 09:38  Schwifty  阅读(142)  评论(0)    收藏  举报