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