LeetCode | 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 difference between nums[i] and nums[j] is at most t and the difference between i and j is at most k.

要求:|nums[i]-nums[j]|<=t,且|i-j|<=k,用hashmap就没意义了,因为不是找相同,而是找相近了。
tag提示binary search tree,而java中的tree容器有:TreeSet与TreeMap。其中,TreeSet是基于TreeMap实现的,底层使用红黑树,属于平衡二叉树。TreeSet保证基本操作(add,remove,contains)时间复杂度为O(logn),且内部元素有序。(hashmap不保证有序,且两次返回的顺序也不一定相同,但是它对于get与put操作能保证constant-time performance)


TreeSet<E>中的一些方法:(注意TreeSet中的元素时有序的)
ceiling(E e):大于等于e中的最小值
floor(E e):  小于等于e中的最大值
higher(E e)/lower(E e):大于/小于 e中的 最小/最大 的元素
first():     返回最小的元素
last():      返回最大的元素

正确的代码:

public class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        
        if(k<1 || t<0){
            return false;
        }
        
        boolean result = false;
        TreeSet<Integer> mySet = new TreeSet<Integer>();
        
        for(int i=0; i<nums.length; i++){
            Integer high = mySet.ceiling(nums[i]);      //大于等于nums[i]的最小值
            Integer low = mySet.floor(nums[i]);         //小于等于nums[i]的最大值
            if(high!=null && high<=t+nums[i] || low!=null && nums[i]<=t+low){
                result = true;
                break;
            }
            mySet.add(nums[i]);
            if(i>=k){                                  //使set的大小始终小于等于k,起到滑动窗口的作用
                mySet.remove(nums[i-k]);
            }
            
        }
        
        return result;
    }
}
注意,在上面的代码中:
if(high!=null && high-nums[i]<=t || low!=null && nums[i]-low<=t){
    result = true;
    break;
}
这样的写法不行,leetcode有个testcase是[-1,2147483647], 1, 2147483647。按上面的写法会发生溢出。。。



下面是有错的代码:(用map< nums[i], index >来记录已遍历过的值及其index,这种想法是可行的,但在本题的条件下,就得额外考虑一些东西了)

public class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        
        if(k<1 || t<0){
            return false;
        }
        
        boolean result = false;
        TreeMap<Integer, Integer> myMap = new TreeMap<Integer, Integer>();
                                                        //找了很久,终于找到这段代码哪里错了。。。。
        for(int i=0; i<nums.length; i++){               //[10,100,11,9],k=1,t=2,当循环到num[3]=9时,得low=null,high=<10,0>
            Integer high = myMap.ceilingKey(nums[i]);   //而非<11,2>,代码判断t满足,但是k不满足,从而result=false...
            Integer low  = myMap.floorKey(nums[i]);     
            if(high!=null && high<=t+nums[i] && i-myMap.get(high)<=k){
                result = true;                          //故在上面的情况下,仅仅找到一个high就判断当前遍历的nums[i]能否返回true是不够的
                break;                                  //还要找到比high大的最小元素再判断,然后循环找下去,甚至当对元素间距k要求很松时,
            }                                           //可能得在map中找到所有比nums[i]大的元素判断
            if(low!=null && nums[i]<=t+low && i-myMap.get(low)<=k){
                result = true;
                break;
            }
            
            myMap.put(nums[i], i);
        }
        
        return result;
    }
}


原文地址:https://www.cnblogs.com/dosmile/p/6444422.html