1. 程式人生 > 實用技巧 >220. Contains Duplicate III(核心:set陣列有序/桶排序)

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

Given an array of integers, find out whether there are two distinct indicesiandjin the array such that theabsolutedifference betweennums[i]andnums[j]is at mosttand theabsolutedifference betweeniandjis at mostk.

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