leetcode 220. 存在重复元素 III
给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在两个下标 i 和 j,使得
abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k ,如果存在则返回 true,不存在返回 false。
解题关键的滑动窗口大小, [ x - t , x + t ]
方法一:
对于序列中每一个元素x左侧最多k个元素,如果这k个元素中有一个元素 nums[ ] 落在 [ x - t , x + t ] 中,证明可以,同理,如果x右侧有最多k个元素,这k个元素中有一个元素 nums[ ] 落在 [ x - t , x + t ] 中,证明也可以,对于 [3 7 9 1 8 2 5] , 1和 8相邻,向他们的左边看,如果k=3 有2个数字重合,即左边k个数字,相邻的两个数字有k-1个相同,于是有一个这样的滑动窗口,每次遍历到x时候,我们判断[x-k,x]中的 num[]是否落在 [ x - t , x + t ]中即可
如果使用队列维护滑动窗口内的元素,由于元素是无序的,我们只能对于每个元素都遍历一次队列来检查是否有元素符合条件。如果数组的长度为 nn,则使用队列的时间复杂度为 O(nk)O(nk),会超出时间限制。
因此我们希望找到一个这样的数据结构:
支持添加和删除指定元素,否则我们无法维护滑动窗口
内部元素有序,支持二分查找的操作,这样我们可以快速判断滑动窗口中是否存在元素满足条件,具体说,对于元素nums[i],当我们希望判断滑动窗口中是否存在某个数 nums[j] 落在 区间[ num[ i ] - t , num[ i ] + t ]中,只需要判断滑动窗口中所有大于等于 num[ i ] - t 的元素 中的最小值 <= num[ i ] + t ,这里的滑动窗口是我们在给定的数据之滑动的,区间[ num[ i ] - t , num[ i ] + t ]是用来判断的条件。
使用有序集合来支持这些操作
实现:我们在有序集合中查找>= x-t的最小元素y,如果y存在,且 y<=x+t 我们就找到了一对符合条件的元素,完成检查后,我们将x插入到有序集合中,如果有序集合数量超过了k,我们将有序集合中最早被插入的元素删除即可(这一步就是滑动)。有序集合C++用set,滑动窗口就可以将给定数据中的数值插入set中,然后再判断set中 大于num[i] - t中的最小值是否小于 num[i] + t ,如果有,就返回true ,没有就返回false。
class Solution{
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
int length = nums.size();
set<int> rec; //定义set有序 滑动窗口
for (int i = 0; i < length; i++) {
//二分查找一个有序数列,返回第一个大于等于x的数,没有找到的话就返回末尾的迭代器位置
auto iter = rec.lower_bound(max(nums[i], INT_MIN + t) - t); //遍历滑动窗口
if (iter != rec.end() && *iter <= min(nums[i], INT_MAX - t) + t) {
return true;
}
rec.insert(nums[i]); //滑动窗口的变化
if (i >= k) {
rec.erase(nums[i - k]);
}
}
return false;
}
};
c++ lower_bound()函数用于在指定区域内查找不小于目标值的第一个元素 >=,也就是说,使用该函数在指定范围内查找某个目标值时,最终查找到的不一定是和目标值相等的元素,还可能是比目标值大的元素。
lower_bound在set中用法:
二分查找一个有序数列,返回第一个大于等于x的数,如果没找到,返回末尾的迭代器位置