滑动窗口+有序集合
维护滑动窗口,向右扩大右窗口,新数加入有序集合,题目要求abs(nums[i] - nums[j]) <= t
,找两数之差的绝对值小于t
,相当于在窗口里找大小尽可能接近的两个数,固定其中一个数(新数),枚举窗口的数,得到另一个数。在有序集合里,可以二分优化枚举,找窗口内,大于等于新数的第一个数,位置x
,在有序集合里,小于新数的第一个数的位置是x-1
。这两个数,就是窗口内最接近新数的数。判断abs(*x - nums[j]) <= t
和abs(*(x-1) - nums[j]) <= t
即可。
提示 : 窗口最大长度 k + 1 k+1 k+1 ,超过这个长度时,需要删除左窗口的值,向右缩小左窗口。
提示 : 使用有序集合的原因:利用有序集合 l o g n logn logn 时间进行插入、删除、查询操作,降低枚举窗口内元素的时间瓶颈。
提示 : 往有序集合里加入无穷大的哨兵,避免边界问题(空集合查询)。
题目描述
核心代码
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
typedef long long LL;
set<LL> S;
S.insert(1e18),S.insert(-1e18);
for(int i = 0,j = 0;j<nums.size();j++){
if(j-i>k) S.erase(S.find(nums[i++]));
auto x = S.lower_bound(nums[j]);
if(abs(*(x--) - nums[j])<=t) return true;
if(abs(*x - nums[j])<=t) return true;
S.insert(nums[j]);
}
return false;
}
};
- 时间复杂度 : O ( n × l o g ( m i n ( n , k ) ) ) O(n\times log(min(n,k))) O(n×log(min(n,k))) , n n n 是数字数量,遍历数字的时间复杂度 O ( n ) O(n) O(n) ,维护滑动窗口(有序集合),插入删除和查询的时间复杂度都是 O ( l o g ( m i n ( n , k ) ) ) O(log(min(n,k))) O(log(min(n,k))) ,二者是乘积关系,时间复杂度 O ( n × m i n ( l o g ( n , k ) ) ) O(n\times min(log(n,k))) O(n×min(log(n,k))) 。
- 空间复杂度 : O ( m i n ( n , k ) ) O(min(n,k)) O(min(n,k)) ,滑动窗口的空间复杂度 O ( m i n ( n , k ) ) O(min(n,k)) O(min(n,k)) 。
AC
致语
- 理解思路很重要!
- 欢迎读者在评论区留言,墨染看到就会回复的。