220. Contains Duplicate III(核心:set数组有序/桶排序)

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;
    }
};

 

posted on 2020-12-06 11:51  wsw_seu  阅读(85)  评论(0编辑  收藏  举报

导航