输入一个数组,求是否存在abs(i-j)<=k使得abs(nums[i]-nums[j])<=t
既要比较元素值又要比较下标,可以二重循环,但是这题的本意肯定不在此
我的做法:
set自定义数据类型和比较函数,如果nums[i]不同就按照升序排列,如果nums[i]相同按照下标降序排列
然后用两个指针,开始都指向开头,it指向当前判断的元素,it2指向it之前的元素:
1.如果it和it2的差值>t,说明it2指向的元素过时了,it2++
2.如果差值<=t:
1)如果下标满足条件,return true;
2)否则it++这里就是为什么自定义比较函数时按照下标降序,非常重要:
假设xxxxyyyzzz,指针it2,指向第一个x,指针it指向第一个y,且差值<=k,但是下标不满足<=t,这时该移动it2还是it?
如果是按下标升序排列,那么为了得到结果,(情况1)因为有可能中间的x的下标满足条件,所以应该移动it2,(情况2)但是万一所有x的下标都不满足条件,但是第一个x和后面的某个z满足条件,而我们已经移动了it2,导致第一个x不能参与计算,这就有错了。
也就是移动第一个指针的唯一条件是当元素差值>k,否则它都可能对后面的结果做出贡献而不能移动
所以这里应该移动it,不过移动it不就把情况1跳过了吗,也出错
所以这里下标降序排列,证明它的合法性:
1.假设第一个x的下标比第一个y的下标小,这时it往后移动,使得y的下标更小,更接近x的下标,这个思路是对的
2.假设第一个x的下标比第一个y的下标大,这时it往后移动,获得的y得下标会更大,离x的下标会更远,不可能得到true,但这个思路也是对的,为什么呢,因为最接近x的下标的就是前面的y的下标,它的不满足条件,后面的肯定不满足,不过我们还是移动it,这时移动it不是为了得到满足条件的情况,只是移动it而已,目的其实是移动到z
综上,即证
class Solution {
private:
struct node{
long long x,indx;
node(long long x=0,long long indx=0):x(x),indx(indx){}
bool operator<(const node &n)const{
if(x<n.x) return true;
if(x==n.x) return indx>n.indx;
return false;
}
};
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
int len=nums.size();
if(len==0||len==1) return false;
set<node> s;
for(int i=0;i<len;i++){
node no(nums[i],i);
s.insert(no);
}
set<node>::iterator it=s.begin();
node no=*it;
set<node>::iterator it2=s.begin();
it++;
while(1){
if(it->x-it2->x<=t&&abs(it->indx-it2->indx)<=k) return true;
if(it->x-it2->x>t){
it2++;
if(it2==it) it++;
if(it==s.end()) break;
// no.x=it->x;
// no.indx=it->indx;
}
else{
it++;
if(it==s.end()) break;
}
}
// it2++;
// while(it2!=it){
// if(it->x-it2->x<=t&&abs(it->indx-it2->indx)<=k) return true;
// it2++;
// }
return false;
}
};