剑指 Offer II 057. 值和下标之差都在给定的范围内(桶排序)

该博客介绍了如何利用桶排序算法解决LeetCode的一道题目,即判断数组中是否存在两个不同下标的元素,其差的绝对值小于等于t且下标差的绝对值小于等于k。文章详细阐述了解题思路,包括固定滑动窗口和分桶策略,并提供了具体的Java代码实现。通过滑动窗口限制和桶内元素的关系,确保了在满足条件的情况下找到符合条件的元素对。

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

文章目录


题目

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

如果存在则返回 true,不存在返回 false。

示例 1:

输入:nums = [1,2,3,1], k = 3, t = 0
输出:true

示例 2:

输入:nums = [1,0,1,1], k = 1, t = 2
输出:true

示例 3:

输入:nums = [1,5,9,1,5,9], k = 2, t = 3
输出:false

提示:

0 <= nums.length <= 2 * 104
-231 <= nums[i] <= 231 - 1
0 <= k <= 104
0 <= t <= 231 - 1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/7WqeDu


一、桶排序

解题思路:
第一步:
可以利用固定滑动窗口大小来满足abs(i - j) <= k。
在遍历初期不固定滑动窗口,滑动窗口逐渐增大至当 i >=k时,只保留区间 [i-k,i] 的值。

第二步:
可以根据数组中元素的值进行分桶存放,假设桶按位 t 划分,则有:
编号0的桶:存放【0,t】的数
编号1的桶:存放【t+1,2t+1】的数
以此类推。。。
对于负数
编号-1的桶:存放【t-1,-1】的数
编号-2的桶:存放【2t-2,-t-2】的数
可以发现桶ID有:
n>=0 : ID=n/t+1; (例如t=2。区间【0,1,2】内的数除以(t+1)都为0)
n<0 : ID=(n+1)/(t+1) -1
这样我们可以确定一个桶内的元素都符合abs(nums[i] - nums[j]) <= t

定义map存放元素的桶id
①遍历到当前元素时,如果发现桶内已存在元素,则直接返回true
②如果不存在则检查相邻桶内是否存在一个元素使得abs(nums[i] - nums[j]) <= t
因为相邻桶内也可能存在两数差绝对值小于等于t
③当相邻桶内不存在两数差绝对值小于等于t,则把当前桶id存进map,继续遍历下个数

重要一点:
符合一个桶的数值有多个,而hash表只能存放某个桶id的一个数值,
为什么要用hash表存放桶id,是否存在遍历到一个数时把桶内其他元素覆盖?
不会覆盖,因为在滑动窗口的约束下,map中存放的值都是窗口内的值,而当遍历到某个值时,如果当前桶内存有元素,那会直接返回true,所以桶内存放的元素要么没有,要么就只有一个

代码如下:

class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        int n = nums.length;
        Map<Long,Long> map = new HashMap<>();
        long w = (long) t+1;
        for(int i=0;i<n;i++){
            long id = getId(nums[i],w);
            //当前桶存在元素,直接返回true
            if(map.containsKey(id)) return true;
            //查看相邻桶是否有差值小于等于t
            if(map.containsKey(id-1)&&Math.abs(map.get(id-1)-nums[i])<w) return true;
            if(map.containsKey(id+1)&&Math.abs(map.get(id+1)-nums[i])<w) return true;
            map.put(id,(long)nums[i]);
            //约束滑动窗口大小,大小为k,当i逐渐自增大于k时,把 i-k 下标的元素的桶删掉
            //为什么是 i>=k? i-k?
            //因为如果当前i=k,在当前遍历是ok的,但在for循坏下一轮时就超过,所以提前删除
            //i-k一样的道理
            if(i>=k){
                map.remove(getId(nums[i-k],w));
            }
        }
        return false;
        
    }
    public long getId(long x,long w){
        if(x>=0) return x/w;
        else return (x+1)/w - 1;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值