LC220. 存在重复元素 III

题目

给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。
如果存在则返回 true,不存在返回 false。
提示:

    0 <= nums.length <= 2 * 10^4
    -2^31 <= nums[i] <= 2^31 - 1
    0 <= k <= 10^4
    0 <= t <= 2^31 - 1

思路

题目很简单,在数组nums内,对于索引值i找到i-k到i+k中符合abs(nums[i] - nums[j]) <= t条件的值。
对于这种具有固定子数组范围,可以采用滑动窗口。
当i在窗口最右边,i后面可以有k个数(到i-k为止)
当i滑到窗口最左边,i前面可以有k个数(到i+k为止)
因此滑动窗口大小只需要k即可
当窗口沿着数组从左到右滑动时,窗口最前面添加数组当前索引内容,并丢掉窗口内最后一个数,保持窗口大小保持k不变。

但是滑动时,对于窗口前的数x,都需要将它与窗口内其他数对比,使abs(nums[i] - nums[j]) <= t。窗口到达nums[i]时,nums[i]只需要找比自己小t之内的数。因为窗口更新后新数值nums[j]会和nums[i]对比是不是比nums[j]小t,这就相当于nums[i]找比自己大t之内的数了。
这时就遇到了问题:nums[i]需要在窗口内找比自己小t的数,如果采用遍历一遍窗口值进行对比,最后会出现超时(自身尝试过)
因此就不能采用再叠加一次循环。

这时可以采用有序集合set。我们可以将窗口中数组存入set,并在遍历数组时进行更新。
存入set后,数值会进行排序,有利于查找。set排序采用红黑树,时间复杂度为O(logn),,不会出现超时现象。
同时 set有一个lower_bound(val)函数,时间复杂度为O(logn) 可以返回第一个大于或等于 val 的元素的双向迭代器。这正符合我们查找值的需要。
对于窗口最前端nums[i],通过lower_bound(nums[i]-t)查找第一个大于或等于nums[i]-t的值,如果值存在并且小于nums[i]+t,说明为true。

同时需要注意nums[i]数值在int范围内,nums[i]-t和nums[i]+t可能会溢出,需要进行判断。或者转化为long long数据类型

代码

class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        int len = nums.size();
        set<int> st;
        for(int i=0;i<len;i++){
            auto iter = st.lower_bound(max(nums[i], INT_MIN+t)-t);   
            // max(nums[i], INT_MIN+t)-t是进行溢出判断,如果nums[i]-t小于INT_MIN,则将其赋值为INT_MIN
            if(iter!=st.end() && *iter<=min(nums[i], INT_MAX-t)+t)
            // 判断是否存在大于或等于nums[i]-t的值,并且要小于nums[i]+t
                return true;
            st.emplace(nums[i]);
            if(k<=i)  //窗口大小够k个了,排掉最后一个
                st.erase(nums[i-k]); 
        }
        return false;
    }
}

你可能感兴趣的:(#,数组,leetcode,滑动窗口,set,排序,c++)