zoukankan      html  css  js  c++  java
  • LeetCode #220. Contains Duplicate III 数组 双指针 滑动窗口

    Description


    Given an array of integers, find out whether there are two distinct indices i and j in the array such that the absolute difference between nums[i] and nums[j] is at most t and the absolute difference between i and j is at most k.

    Example 1:

    Input: nums = [1,2,3,1], k = 3, t = 0
    Output: true
    

    Example 2:

    Input: nums = [1,0,1,1], k = 1, t = 2
    Output: true
    

    Example 3:

    Input: nums = [1,5,9,1,5,9], k = 2, t = 3
    Output: false
    



    思路


    解法一

    之前做过#219,所以一开始的想法是使用和 #219 一样的解法。但是会发现,如果使用一个外层 for 和 map 记录下标,一个内层 for 遍历t,就太暴力了,OJ会超时,所以得想一个取巧的办法。

    一个好的方法就是滑动窗口。我们利用 i 和 j 维护一个大小为 k 的窗口,i 和 j 分别为窗口的左右边界。窗口内的元素值存储在一个 set 里。利用 lower_bound() 函数得到可能符合条件的值x,当 |x - nums[i]| <= t 时,说明找到了想要的元素。

    这里有一个比较 tricky 的点是,也许我们会以为 nums 可能会有多个相同值的元素,使用 set 的话或许会漏掉它们。但实际上我们不需要使用multi_set,只需要使用set。

    比如nums = [1, 1, 3, 2],k = 2,t = 1。乍一看可能会认为当滑动窗口从 [1, 1, 3] 转变到 [1, 3, 2] 的过程中 set 会误删 1 而导致第二个 1 缺失了,但实际上在滑动窗口从 [1] 扩大到 [1, 1] 的过程中时,重复数字就已被发现并 return 了。

    class Solution {
    public:
        bool containsNearbyAlmostDuplicate(const vector<int> &nums, int k, int t) {
            if (nums.empty()) return false;
    
            bool has_dup = false;
            set<int> ij_set;  // save elements between i and j
    
            for (int i = 0, j = 0; i < nums.size(); ++i) {
                if (i - j > k) {
                    ij_set.erase(nums[j]);
                    ++j;
                }
    
                auto iter = ij_set.lower_bound(nums[i] - t);
                if (iter != ij_set.end() && abs(nums[i] - *iter) <= t) {
                    has_dup = true;
                    break;
                }
    
                ij_set.insert(nums[i]);
            }
    
            return has_dup;
        }
    };
    




    运行完上面的代码发现过不了,因为test case 中有大数的 case,比如:

    [2147483647,-2147483647]
    1
    2147483647
    

    所以我们需要改一下上面的代码,将可能溢出的部分改成 long long。

    时间复杂度:O(nlgn) = 遍历数组 O(n) *(Set删除 O(lgn) + Set查找 O(lgn) + Set插入 O(lgn))
    空间复杂度:O(n) = 新建Set

    耗时 28 ms, Memory 8.9 MB, ranking 39.72%

    class Solution {
    public:
        bool containsNearbyAlmostDuplicate(const vector<int> &nums, int k, int t) {
            if (nums.empty()) return false;
    
            bool has_dup = false;
            set<long long> ij_set;  // save elements between i and j
    
            for (int i = 0, j = 0; i < nums.size(); ++i) {
                if (i - j > k) {
                    ij_set.erase(nums[j]);
                    ++j;
                }
    
                auto iter = ij_set.lower_bound((long long)nums[i] - t);
                if (iter != ij_set.end() && abs(nums[i] - *iter) <= t) {
                    has_dup = true;
                    break;
                }
    
                ij_set.insert(nums[i]);
            }
    
            return has_dup;
        }
    };
    



    参考




  • 相关阅读:
    Beta版本冲刺第二天 12.6
    Beta版本冲刺第一天 12.5
    Linux学习(2)—— 图形化界面
    Linux学习(1)—— 虚拟机安装Linux系统
    IntelliJ IDEA使用
    spring+springmvc+hibernate 整合
    新的篇章
    软件工程实践总结作业——个人作业
    Beta版本冲刺———第七天
    Beta版本冲刺———第六天
  • 原文地址:https://www.cnblogs.com/Bw98blogs/p/12739801.html
Copyright © 2011-2022 走看看