题目:
给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。
如果存在则返回 true,不存在返回 false。
输入:nums = [1,2,3,1], k = 3, t = 0
输出:true
输入:nums = [1,0,1,1], k = 1, t = 2
输出:true
输入:nums = [1,5,9,1,5,9], k = 2, t = 3
输出:false
分析:
1.最直观的解法就是逐一扫描数组中的每个数字,对于每个数字nums[i],需要逐一扫描数组中的每个数字。对于每个数字nums[i],需要逐一检查在它前面的k个数字是否存在从nums[i]-t到nums[i]+t的范围内的数字,如果存在则返回true。由于数组中的每个数字都要和k个数字进行比较,如果数组长度为n,那么解法的时间复杂度是O(nk)。
2.可以利用Treeset来优化算法,因为这个容器只需要保存数字,所以可以用Treeset或TreeMap适用于的场景。因为容器只需要保存每个数字nums[i]前面的k个数字,变量set是一个TreeSet,它的大小是k,因此空间复杂度是O(k),对于查找,删除和添加操作的时间复杂度都是O(logk),因此对于一个长度为n的数组而言,它的时间复杂度O(nlogk)。
3.还可以换一个思路来解决这个问题,该题关心的是差的绝对值小于或等于t的数字,因此可以将数字放入若干个大小为t+1的桶中,比如将从0到t的数字放入编号为0的桶中,从t+1到2t+1的数字放入编号为1的桶中,其他数字一次类推,好处是如果两个数字被放入同一个桶中,它们的差的绝对值一定小于或等于t。还是逐个扫描数组中的数字,如果当前扫描到数字num,那么它将放入编号为id的桶中,如果桶中之前已经有数字,那么就找到两个差的绝对值小于或等于t的数字。如果桶中没有数字,则判断编号相邻的两个桶中是否存在与num的差的绝对值小于或等于t的数字。因为其他桶中的数字与num的差的绝对值一定大于t,所以不需要判断其他的桶中是否有符合条件的数字。哈希表的查找,添加和删除操作时间复杂度为O(1),时间复杂度为O(n),空间复杂度为O(k)。
代码:
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
public class ContainsNearbyAlmostDuplicate {
public static void main(String[] args) {
int[] nums = {1,-5,9,1,5,9};
boolean b = containsNearbyAlmostDuplicate2(nums, 2, 3);
System.out.println(b);
}
public static boolean containsNearbyAlmostDuplicate1(int[] nums,int k,int t){
TreeSet<Long> set = new TreeSet<>();
for (int i = 0; i < nums.length; i++) {
Long lower = set.floor((long)nums[i]);
//nums[i] <= lower+t
if (lower!=null&&lower >=(long)(nums[i] - t)){
return true;
}
Long upper = set.ceiling((long)nums[i]);
//nums[i] >=upper-t
if (upper !=null && upper <= (long)nums[i] + t){
return true;
}
set.add((long)nums[i]);
if (i >=k){
set.remove((long)nums[i-k]);
}
}
return false;
}
public static boolean containsNearbyAlmostDuplicate2(int[] nums,int k,int t){
Map<Integer, Integer> buckets = new HashMap<>();
int bucketSize = t+1;
for (int i = 0; i < nums.length; i++) {
int num = nums[i];
//计算桶对应的id数
int id = getBucketID(num,bucketSize);
if (buckets.containsKey(id)
||(buckets.containsKey(id-1)&&buckets.get(id-1)+t >= num)
||(buckets.containsKey(id+1)&&buckets.get(id+1)-t <= num)){
return true;
}
buckets.put(id,num);
if (i >= k){
buckets.remove(getBucketID(nums[i-k],bucketSize));
}
}
return false;
}
private static int getBucketID(int num, int bucketSize) {
return num>=0 ? num/bucketSize : (num+1)/bucketSize - 1;
}
}