leetcode 存在重复元素 III

问题

给你一个整数数组 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;
        //创建一个有序集合,充当滑动窗口
        TreeSet<Long> set = new TreeSet<>();
        //判断是否在滑动窗里存在小于等于给定范围的数
        for(int i=0;i<n;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;
    }

你可能感兴趣的:(leetcode,算法,leetcode,算法,滑动窗口)