• 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
    
    • 1
    • 2

    示例 2:

    输入:nums = [1,0,1,1], k = 1, t = 2
    输出:true
    
    • 1
    • 2

    示例 3:

    输入:nums = [1,5,9,1,5,9], k = 2, t = 3
    输出:false
    
    • 1
    • 2

    提示:

    • 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 set = new HashSet<>();
    //         Map 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;
    //     }
    // }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    桶排序

    题解: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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    getIdx部分的解释:宫水三叶

  • 相关阅读:
    PythonStudy5
    两数之和(Java版)
    beamManagement(四)connected mode UL training
    70个让你笑爆肚皮的程序员段子
    Qt Widget 删除之后还会显示 问题
    MySql模糊查询大全
    【数据结构初阶-复杂度】运行 只用了3ms...我真牛(得意
    使用RS485芯片进行串口通讯
    航顺主流替代型HK32F103系列
    谷粒学院——Day09【整合阿里云视频点播】
  • 原文地址:https://blog.csdn.net/qq_42958831/article/details/128138136