Question

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.


本题难度Medium。有2种算法分别是: 遍历法(超时)和二叉搜索树

注意

t可以等于0

1、遍历法

复杂度

时间 O(N*k) 空间 O(1)

思路

对每个待考察数x,搜索其前面的k个数看是否有满足条件:​​abs(x-nums[j])<=t​

该方法是 Time limit exceeded.

注意

第11行不要写为:​​if(Math.abs(num-nums[i-j])<=t)​​,如果遇到

Input:
[-1,2147483647]
1
2147483647

就会出错,原因是差值超出整型范围。

代码

public class Solution {
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
//require
if(nums==null||nums.length<2||k<1)
return false;
//invariant
for(int i=0;i<nums.length;i++){
int num = nums[i];
for(int j=1;j<=k;j++){
if(i-j<0)break;
if(Math.abs((long)num-nums[i-j])<=t)
return true;
}
}
//ensure
return false;
}
}

2、二叉搜索树

复杂度

时间 O(Nlogk) 空间 O(k)

思路

要求判断之前是否存在差值小于t的数字,我们需要知道在当前数字x两边的数字,即最大的小于x的数字和最小的大于x的数字。二叉搜索树有也有这样的性质,它的左子树的最右节点是最大的较小值,右子树的最左节点是最小的较大值。这里我们用TreeSet这个类,它实现了红黑树,并有集合的性质,非常适合这题。我们同样也是维护一个大小为k的TreeSet,多余k个元素时把最早加入的给删除。用ceiling()和floor()可以找到最小的较大值和最大的较小值。

注意

第6行不要写成​​Set<Integer> set = new TreeSet<>();​​,方法ceiling和floor并不是超类Set的方法。

代码

public class Solution {
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
//require
if(nums==null||nums.length<2||k<1)
return false;
TreeSet<Integer> set = new TreeSet<>();
//invariant
for(int i=0;i<nums.length;i++){
int x = nums[i];
//return the min in elements which >= x
if(set.ceiling(x)!=null&&set.ceiling(x)<=x+t)return true;
//return the max in elements which <= x
if(set.floor(x)!=null&&x<=set.floor(x)+t)return true;
set.add(x);
if(set.size()>k)set.remove(nums[i-k]);
}
//ensure
return false;
}
}

参考

​​[Leetcode] Contains Duplicate 包含重复​​