题目描述
题解
对于位置 [i-indexDiff,i+indexDiff]
,因此设置一个滑动窗口,窗口大小为indexDiff
,从数组位置0开始进行滑动。这样,当遍历到某个位置 indexDiff
个元素。此时我们可以检查窗口中是否有某个数据落在了[x-valueDiff, x+valueDiff]
之间。
如果使用队列维护滑动窗口内的元素,由于元素是无序的,我们只能对于每个元素都遍历一次队列来检查是否有元素符合条件。如果数组的长度为
因此我们希望能够找到一个数据结构维护滑动窗口内的元素,该数据结构需要满足以下操作:
1.支持添加和删除指定元素的操作,否则我们无法维护滑动窗口;
2.内部元素有序,支持二分查找的操作,这样我们可以快速判断滑动窗口中是否存在元素满足条件,具体而言,对于元素 [x-valueDiff, x+valueDiff]
之间,我们只需查找区间中大于等于 x-valueDiff
的最小元素是否小于等于 x+valueDiff
对于上述要求,C++中的set可以满足。
实现方面,我们在有序集合中查找大于等于 x−valueDIff
的最小的元素 y
,如果 y
存在,且 y≤x+valueDiff
,我们就找到了一对符合条件的元素,则可返回true。反之,我们将 x
插入到有序集合中,如果有序集合中元素数量超过了 indexDiff
,我们将有序集合中最早被插入的元素删除即可。
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int indexDiff, int valueDiff)
{
int n = nums.size();
set<long long> rec;
for(int i = 0; i < n; i++)
{
set<long long> :: iterator it = rec.lower_bound(nums[i] - valueDiff);
if(it != rec.end() && *it <= (nums[i] + valueDiff))
return true;
//i从0开始
rec.insert(nums[i]);
if(i >= indexDiff)
rec.erase(nums[i - indexDiff]);
}
return false;
}
};
时间复杂度
空间复杂度
滑动窗口适用于求解连续k个数中搜寻满足某个条件的数
的问题
题解2
然而,该题还有另一种巧妙的方法。可以利用桶排序的思想解决本题,我们按照元素的大小进行分桶,维护一个滑动窗口内的元素对应的元素。
对于元素 [x-valueDiff,x+valueDiff]
. 为此我们采用分桶方法,将每个桶的大小设置为 valueDiff+1
。如果两个元素同属一个桶,那么这两个元素必然符合条件。如果两个元素属于相邻桶,那么我们需要校验这两个元素是否差值不超过 valueDiff
。如果两个元素既不属于同一个桶,也不属于相邻桶,那么这两个元素必然不符合条件。
具体地,我们遍历该序列,假设当前遍历到元素
从实现方面,由于设置了桶大小为valueDiff+1
,因此需要给int范围内实数进行标号,如0-valueDiff
范围内的实数标为0,即放在第一个桶中。
打个比方:
这里假设t=10
,则根据我们的求解思路,必须要使每个桶的容纳范围为11,即桶的容量要设置为11。对于大于等于0的数,显然用该数除以11即可得到这个数字正确的桶编号,由于C++中正小数负小数进行整除时,都是向0取整的,所以对于负数,应当设计出不同于正数的编号方法,研究得到如下编号方法:
当x>=0
时, id = x/(t+1)
; 当x<0
时, id = (x+1)/(t+1)-1
.
这样的分组效果如下: ... -2号桶:-12 ~ -22; -1号桶:-1 ~ -11; 0号桶: 0~10; 1号桶: 11 ~ 20 ...
class Solution {
public:
int getID(int x, long w) {
return x < 0 ? (x + 1) / w - 1 : x / w;
}
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
unordered_map<int, int> mp;
int n = nums.size();
for (int i = 0; i < n; i++) {
long x = nums[i];
int id = getID(x, t + 1);
if (mp.count(id)) {
return true;
}
if (mp.count(id - 1) && abs(x - mp[id - 1]) <= t) {
return true;
}
if (mp.count(id + 1) && abs(x - mp[id + 1]) <= t) {
return true;
}
mp[id] = x;
if (i >= k) {
mp.erase(getID(nums[i - k], t + 1));
}
}
return false;
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异