LC-220. 存在重复元素 III(滑动窗口+二分,桶排序)

220. 存在重复元素 III

难度困难666

给你一个整数数组 nums 和两个整数 kt 。请你判断是否存在 两个不同下标 ij,使得 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

滑动窗口+二分法

题解:https://leetcode.cn/problems/contains-duplicate-iii/solution/gong-shui-san-xie-yi-ti-shuang-jie-hua-d-dlnv/

class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int indexDiff, int valueDiff) {
        int n = nums.length;
        //使用TreeSet,快速找到 小于等于x的最大值floor 或 小于等于x的最小值ceiling
        TreeSet<Long> set = new TreeSet<>();
        for(int i = 0; i < n; i++){
            Long num = nums[i] * 1L;
            Long l = set.floor(num); //找到小于等于num的最大值
            Long r = set.ceiling(num); // 找到大于等于num的最小值
            if(l != null && num-l <= valueDiff) return true;
            if(r != null && r-num <= valueDiff) return true;
            set.add(num);
            // 一个窗口内不可能存在两个大小相同的数,所以可以直接删除
            // 假设两个数下标是i和j(i < j),当遍历到j时,就会直接返回true了
            if(i >= indexDiff) set.remove(nums[i-indexDiff]*1L);
        }
        return false;
    }
}

// 到i位置检查i-k位置上的元素是否满足要求,超时
// class Solution {
//     public boolean containsNearbyAlmostDuplicate(int[] nums, int indexDiff, int valueDiff) {
//         Set<Integer> set = new HashSet<>();
//         Map<Integer,Integer> map = new HashMap<>();//nums[i] i
//         for(int i = 0; i < nums.length; i++){
//             if(i > indexDiff) set.remove(nums[i-indexDiff-1]);
//             for(int key : set){
//                 if(Math.abs(nums[i]-key) <= valueDiff && Math.abs(map.get(key) - i) <= indexDiff){
//                     return true;
//                 }
//             }
//             set.add(nums[i]);
//             map.put(nums[i],i);
//         }
//         return false;
//     }
// }

桶排序

题解:https://leetcode.cn/problems/contains-duplicate-iii/solution/gong-shui-san-xie-yi-ti-shuang-jie-hua-d-dlnv/

上述解法无法做到线性的原因是:我们需要在大小为 k 的滑动窗口所在的「有序集合」中找到与 u 接近的数。

如果我们能够将 k 个数字分到 k 个桶的话,那么我们就能 O(1) 的复杂度确定是否有 [u - t, u + t]的数字(检查目标桶是否有元素)。

具体的做法为:令桶的大小为 s i z e = t + 1 size = t + 1 size=t+1,根据 u 计算所在桶编号:

  • 如果已经存在该桶,说明前面已有 [u - t, u + t]范围的数字,返回 true
  • 如果不存在该桶,则检查相邻两个桶的元素是有 [u - t, u + t] 范围的数字,如有 返回 true
  • 建立目标桶,并删除下标范围不在 [max(0, i - k), i) 内的桶
class Solution {
    long size;
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        int n = nums.length;
        Map<Long, Long> map = new HashMap<>();
        size = t + 1L;
        for (int i = 0; i < n; i++) {
            long u = nums[i] * 1L;
            long idx = getIdx(u);
            // 目标桶已存在(桶不为空),说明前面已有 [u - t, u + t] 范围的数字
            if (map.containsKey(idx)) return true;
            // 检查相邻的桶
            long l = idx - 1, r = idx + 1;
            if (map.containsKey(l) && u - map.get(l) <= t) return true;
            if (map.containsKey(r) && map.get(r) - u <= t) return true;
            // 建立目标桶
            map.put(idx, u);
            // 移除下标范围不在 [max(0, i - k), i) 内的桶
            if (i >= k) map.remove(getIdx(nums[i - k] * 1L));
        }
        return false;
    }
    long getIdx(long u) {
        return u >= 0 ? u / size : ((u + 1) / size) - 1;
    }
}

getIdx部分的解释:宫水三叶

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值