220. Contains Duplicate III

本文探讨了处理数组中元素差值与位置关系问题的两种高效算法:滑动窗口和桶分类。滑动窗口算法利用set保持元素有序,通过logk时间复杂度更新窗口,快速查找满足条件的元素对。桶分类算法则将元素分布于多个桶中,通过桶间比较降低时间复杂度,适用于元素值域大的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题传送门

问题分析

此问题划分为两个问题:
1、定义一个大小为k的窗口,其最右端元素与左侧任意元素组合是否能满足diff < t的要求。
2、当上一个窗口不存在时,我们需要滑动窗口,此时窗口中的元素发生变化,第一个元素移除,窗口的next元素进入。我们如何利用原有窗口的数据来高效执行,即减少重复计算。

代码

此代码利用set保存有序顺序的特性,来保证每次增加和删除时窗口内部的数据有序,且增加和删除时时间复杂度为logk(具体参考红黑树)。通过此例我们可以知道当我们想要维护一个时常进行增加删除的有序集合时,可以通过set来实现。
在有序数据集中查找符合条件的diff,其采用两步走方式。
|x - windows[last]| <= t ===>-t <= x - windows[last] <= t ===> -t + windows[last]<= x <= t + windows[last]
其中windows[last]表示窗口中最右侧元素,x表示符合条件的目标元素
我们先查找满足 -t + windows[last] <= x 的最小值 mm,
如果mm <= t + windows[last]便表示存在一个合法值。
在set中进行lower_bound查找相当于二叉查找,时间复杂度log k。

 bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
    set<int> window; // set is ordered automatically 
    for (int i = 0; i < nums.size(); i++) {
        if (i > k) window.erase(nums[i-k-1]); // keep the set contains nums i j at most k
        // |x - nums[i]| <= t  ==> -t <= x - nums[i] <= t;
        auto pos = window.lower_bound(nums[i] - t); // x-nums[i] >= -t ==> x >= nums[i]-t 
        // x - nums[i] <= t ==> |x - nums[i]| <= t    
        if (pos != window.end() && *pos - nums[i] <= t) return true;
        window.insert(nums[i]);
    }
    return false;
}

下面的答案采用桶分类的方式进行求解,其时间复杂度更低
原始答案解析
此方法在处理时使用bucket进行分类,bucket按照 t + 1为间隔进行分割。
1、同一个bucket,其值必定只差小于 t
2、相邻bucket,可能存在,此时需要进行额外判断
3、bucket相距大于1,则其间隔大于 t + 1。

class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        int n = nums.size();
        
        if(n == 0 || k < 0  || t < 0) return false;
        
        unordered_map<int,int> buckets;
        
        for(int i=0; i<n; ++i) {
            int bucket = nums[i] / ((long)t + 1);
            
			// For negative numbers, we need to decrement bucket by 1
			// to ensure floor division.
			// For example, -1/2 = 0 but -1 should be put in Bucket[-1].
			// Therefore, decrement by 1.
            if(nums[i] < 0) --bucket;
            
            if(buckets.find(bucket) != buckets.end()) return true;
            else {
                buckets[bucket] = nums[i];
                if(buckets.find(bucket-1) != buckets.end() && (long) nums[i] - buckets[bucket-1] <= t) return true;
                if(buckets.find(bucket+1) != buckets.end() && (long) buckets[bucket+1] - nums[i] <= t) return true;
                
                if(buckets.size() > k) {
                    int key_to_remove = nums[i-k] / ((long)t + 1);
                    
                    if(nums[i-k] < 0) --key_to_remove;
                    
                    buckets.erase(key_to_remove);
                }
            }
        }
        
        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值