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
Constraints:
0 <= nums.length <= 2 * 104
-231 <= nums[i] <= 231 - 1
0 <= k <= 104
0 <= t <= 231 - 1
class Solution { public: //|i-j| <= k //|nums[i]-nums[j]| <= t //正常思路:O(N*k)超时 //优化:1、O(N*logk).在 i--i+k中找是否在j使|nums[i]-nums[j]| <= t //如果nums[i]--nums[i+k]有序,即可找 nums[i]-t <= nums[j] || nums[j] >= t+nums[i] //即在有序数组中查找 大于等于nums[i]-t的数 是否存在? 二分(logk)活着lower_boud函数 //难点在于如何构造nums[i] -- nums[i+k]的有序数组? set /* lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。 在从小到大的排序数组中, lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。 upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。 STL的map、multimap、set、multiset都有三个比较特殊的函数,lower_bound、upper_bound、equal_range。 iterator lower_bound (const value_type& val) const; iterator upper_bound (const value_type& val) const; pair<iterator,iterator> equal_range (const value_type& val) const; 上面三个函数是相关联的,equal_range返回两个迭代器,第一个迭代器是lower_bound的返回值,第二个迭代器是upper_bound的返回值。(注意是使用相同val值调用的情况下。) */ bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) { int n = nums.size(); set<long> s; for(int i=0;i<n;i++){ long cur = nums[i]; //为何是 i>k 不是i>=k,因为这里是从后往前处理(这样就不会包含自己),每次遇到nums[i],看nums[i-k]--nums[i-1] //中nums[i]是否能够满足其中某一个的条件 if(i > k) s.erase(nums[i-k-1]); //保持set长度k.容纳i-k-->i-1的元素 //i == k的时候正好是nums[i] 与 set中[i+1]--[k]的比较 auto iter = s.lower_bound(cur-long(t)); //iter值大于等于 cur-long(t) ,同时要小于等于 long(t)+cur if(iter != s.end() && *iter <= long(t)+cur) return true; //插入cur s.insert(cur); } return false; } };
//桶排序:这个很经典 桶宽(t+1)
class Solution { public: //桶排序:所有的数分桶,(t)为桶宽度范围。(t+1): 例如 t = 2, (0,1,2在桶0,3,4,5在桶1 ... //获取nums[i]在哪个桶: 桶id从0开始,num小于0的话,例如 -1 / 2 == 0不行。 long getId(long num,long t) { return num < 0 ? (num+1)/(t+1)-1 : num/(t+1); } long Abs(long a) { if(a < 0) return -a; return a; } //num -- num+t 会在一个桶内,如果 k哥元素范围内必有同范围内的,返回true bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) { //key 为ID ,value为nums[i]。至多一个value,否则就返回了 map<long,long> m; int n = nums.size(); for(int i=0;i<n;i++){ long cur = nums[i]; long id = getId(cur,t); //同一个桶内有元素 if(m.find(id) != m.end()) return true; //相邻桶内也有,相邻桶也必然至多一个元素,否则就满足条件了 if(m.find(id-1) != m.end() && Abs(cur-m[id-1]) <= t){ return true; } //相邻桶内也有 if(m.find(id+1) != m.end() && Abs(cur-m[id+1]) <= t){ return true; } //存入cur m[id] = cur; //删除超过的:为啥是i>=k , 这里保证桶里最多k-1个。 if(i >= k) { auto iter = m.find(getId(nums[i-k],t)); m.erase(iter); } } return false; } };