思路分析:
首先,我选择HashSet这种数据结构。对数组nums遍历时,判断是否存在2个元素的差的绝对值小于等于t,若是,则返回true,否则,将元素加入set中,并维持更新set集的大小最大为k。set集中的元素一定是连续不相同的,若存在相同元素,进入循环体进行判断时,相同元素值差绝对值为0,一定小于t(t>=0),则满足所有条件返回true。
设x为set中的元素,-t <= nums[i] - x <= t 推出:
num[i] - t =< x <= nums[i] + t
查找set中是否存在这个值,对set遍历一次即可。所以时间复杂度为:O(n2),空间复杂度为O(n)。
因为nums[i] + t 可能会超过int范围。注意将其转化为long类型。
参考代码:
//220. Contains Duplicate III
public static boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
HashSet<Integer> set = new HashSet<Integer>();
for(int i=0;i<nums.length;i++){
long l = (long)nums[i]-(long)t,r = (long)nums[i]+(long)t;
for(Integer key:set){
if((long)key>=l&&(long)key<=r)
return true;
}
set.add(nums[i]);
//保持set中最多有k个元素
if(set.size()==k+1){
set.remove(nums[i-k]);
}
}
return false;
}
算法优化:前面提到,该算法的复杂度为O(n2),通过是没问题,但不是高效率的算法。如果将HashSet改为TreeSet,可以优化到O(nlogn)级别,应该很不错了。因为TreeSet中的元素的有序的,java并且提供了相应的方法,其时间复杂度为O(logn),前面推算得到:
num[i] - t =< x <= nums[i] + t 。
java提供的方法如下:
floor(E e) 方法返回在这个集合中小于或者等于给定元素的最大元素,如果不存在这样的元素,返回null.
ceiling(E e) 方法返回在这个集合中大于或者等于给定元素的最小元素,如果不存在这样的元素,返回null
参考代码:`
public static boolean containsNearbyAlmostDuplicate2(int[] nums, int k, int t) {
TreeSet<Long> set = new TreeSet<Long>();
for(int i=0;i<nums.length;i++){
long l = (long)nums[i]-(long)t,r = (long)nums[i]+(long)t;
if(set.ceiling((long) l)!=null&&set.ceiling((long) l)<=r){
return true;
}
set.add((long)nums[i]);
//保持set中最多有k个元素
if(set.size()==k+1){
set.remove((long)nums[i-k]);
}
}
return false;
}