给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。
如果存在则返回 true,不存在返回 false。
示例 1:
输入:nums = [1,2,3,1], k = 3, t = 0 输出:true
示例 2:
输入:nums = [1,0,1,1], k = 1, t = 2 输出:true
示例 3:
输入:nums = [1,5,9,1,5,9], k = 2, t = 3 输出:false
提示:
0 <= nums.length <= 2 * 104 -231 <= nums[i] <= 231 - 1 0 <= k <= 104 0 <= t <= 231 - 1解题思路
对于序列中每一个元素 x 左侧的至多 k个元素,如果这 k 个元素中存在一个元素落在区间[x−t,x+t] 中,我们就找到了一对符合条件的元素。注意到对于两个相邻的元素,它们各自的左侧的 k个元素中有 k−1 个是重合的。于是我们可以使用滑动窗口的思路,维护一个大小为 k 的滑动窗口,每次遍历到元素 x时,滑动窗口中包含元素 xx 前面的最多 k 个元素,我们检查窗口中是否存在元素落在区间 [x - t, x + t]中即可。
这里最为重要的是如何判断滑动窗口里的元素在给定的范围内,如果采用队列,其是无序的,每次遍历都要从头到尾遍历一遍,时间复杂度太高,同时我们要频繁的进行插入与删除操作,也要时间复杂度,那么我们这里采用有序集合来进行这些操作。
实现方面,我们在有序集合中查找大于等于 x−t 的最小的元素 y,如果 y存在,且 y≤x+t,我们就找到了一对符合条件的元素。完成检查后,我们将 x 插入到有序集合中,如果有序集合中元素数量超过了 k,我们将有序集合中最早被插入的元素删除即可。
注意
如果当前有序集合中存在相同元素,那么此时程序将直接返回 true。因此本题中的有序集合无需处理相同元素的情况。
为防止整型int 溢出,我们既可以使用长整型 long,也可以对查找区间 [x - t, x + t]进行限制,使其落在 int 范围内。这里我们采用第一种。
代码public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { int n = nums.length; //创建一个有序集合,充当滑动窗口 TreeSetset = new TreeSet<>(); //判断是否在滑动窗里存在小于等于给定范围的数 for(int i=0;i //找到集合中大于等于 x-t 的最小值,为了避免溢出,都用 long 型 Long ceiling = set.ceiling((long)nums[i]-(long)t); //检查是否存在大于等于 x-t 且小于等于 x+t 的数在集合中 if(ceiling != null && ceiling <= ((long)nums[i]+(long)t)){ return true; } // 加入滑动窗口 set.add((long)nums[i]); // 控制滑动窗口大小为k if(i>=k){ set.remove((long)nums[i-k]);// 移除第一个元素 } } return false; }