220. Contains Duplicate III
下标间距<=k,立马就想到滑动窗口,维护一个大小为k的区间。
方法一:
对于新的数,维护区间大小的同时,需要判断该区间中是否存在 [num-t, num+t] 的数字。由于需要根据大小查找,可以利用大小为 k 的 set 作为维护的区间,set 是有序的,因此可以调用 lower_bound 找到第一个大于等于 num-t 的数字。只要判断该数是否小于等于 num+t 即可(因为只要有一个就行了)。
用 set 是没有问题的,假设 k 区间有重复的元素,重复的元素差值为0,一定<t,一定会返回 true,因此即使有重复元素也可以处理。只不过用 set 的话,erase 的时候不能用 s.size()>k 作为判断条件。本题用multiset做当然也是可以的。
注意一下 int 会越界,要用 long(真恶心)。
class Solution { public: bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) { set<long> s; for (int i=0;i<nums.size();++i){ long num=nums[i]; if (i>k) s.erase(nums[i-k-1]); auto low=s.lower_bound(num-t); if ( low!=s.end() && *low<=num+t ) return true; s.insert(num); } return false; } };
方法二:
利用类似桶排序的方法,将桶作为窗口(上述方法是用set作为窗口)。每个桶的宽度设为t+1,即将数组的数根据大小映射到 ... [0,t],[t+1,2t+1],... 中,只有当两个数在同一个桶中或在相邻的桶中,这两个数差的绝对值才会小于等于t。因此,用这种方法,循环中对于每个数,只要判断这个数本身桶中有没有数,或相邻两个桶中有没有符合要求的数,因此时间复杂度是O(n)。
和桶排序不同的是,每个桶中最多只有一个元素,这是因为如果一个桶之前有一个数,后面的数也在该桶中的话,直接就return true了。因此桶可以直接用hash表来建立。
class Solution { public: long getBucketId(long num, long w){ return num<0 ? (num+1)/w-1 : num/w; } bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) { if (t<0) return false; unordered_map<long,long> buckets; long width=(long)t+1; for (int i=0;i<nums.size();++i){ if (i>k) buckets.erase(getBucketId(nums[i-k-1],width)); long num=nums[i]; long id=getBucketId(num,width); if (buckets.count(id)) return true; if (buckets.count(id-1) && abs(num-buckets[id-1])<width) return true; if (buckets.count(id+1) && abs(num-buckets[id+1])<width) return true; buckets[id] = num; } return false; } };

浙公网安备 33010602011771号