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;
    }
};



参考




posted @ 2020-04-20 19:04  bw98  阅读(162)  评论(0编辑  收藏  举报