剑指offer57:值和下标之差都在给定的范围内

题目:
给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。
如果存在则返回 true,不存在返回 false。
输入:nums = [1,2,3,1], k = 3, t = 0
输出:true
输入:nums = [1,0,1,1], k = 1, t = 2
输出:true
输入:nums = [1,5,9,1,5,9], k = 2, t = 3
输出:false
分析:
1.最直观的解法就是逐一扫描数组中的每个数字,对于每个数字nums[i],需要逐一扫描数组中的每个数字。对于每个数字nums[i],需要逐一检查在它前面的k个数字是否存在从nums[i]-t到nums[i]+t的范围内的数字,如果存在则返回true。由于数组中的每个数字都要和k个数字进行比较,如果数组长度为n,那么解法的时间复杂度是O(nk)。
2.可以利用Treeset来优化算法,因为这个容器只需要保存数字,所以可以用Treeset或TreeMap适用于的场景。因为容器只需要保存每个数字nums[i]前面的k个数字,变量set是一个TreeSet,它的大小是k,因此空间复杂度是O(k),对于查找,删除和添加操作的时间复杂度都是O(logk),因此对于一个长度为n的数组而言,它的时间复杂度O(nlogk)。
3.还可以换一个思路来解决这个问题,该题关心的是差的绝对值小于或等于t的数字,因此可以将数字放入若干个大小为t+1的桶中,比如将从0到t的数字放入编号为0的桶中,从t+1到2t+1的数字放入编号为1的桶中,其他数字一次类推,好处是如果两个数字被放入同一个桶中,它们的差的绝对值一定小于或等于t。还是逐个扫描数组中的数字,如果当前扫描到数字num,那么它将放入编号为id的桶中,如果桶中之前已经有数字,那么就找到两个差的绝对值小于或等于t的数字。如果桶中没有数字,则判断编号相邻的两个桶中是否存在与num的差的绝对值小于或等于t的数字。因为其他桶中的数字与num的差的绝对值一定大于t,所以不需要判断其他的桶中是否有符合条件的数字。哈希表的查找,添加和删除操作时间复杂度为O(1),时间复杂度为O(n),空间复杂度为O(k)。
代码:

import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;

public class ContainsNearbyAlmostDuplicate {
    public static void main(String[] args) {
        int[] nums = {1,-5,9,1,5,9};
        boolean b = containsNearbyAlmostDuplicate2(nums, 2, 3);
        System.out.println(b);
     }
    public static boolean containsNearbyAlmostDuplicate1(int[] nums,int k,int t){
        TreeSet<Long> set = new TreeSet<>();
        for (int i = 0; i < nums.length; i++) {
            Long lower = set.floor((long)nums[i]);
            //nums[i] <= lower+t
            if (lower!=null&&lower >=(long)(nums[i] - t)){
                return true;
            }
            Long upper = set.ceiling((long)nums[i]);
            //nums[i] >=upper-t
            if (upper !=null && upper <= (long)nums[i] + t){
                return true;
            }
            set.add((long)nums[i]);
            if (i >=k){
                set.remove((long)nums[i-k]);
            }
        }
        return false;
    }
    public static boolean containsNearbyAlmostDuplicate2(int[] nums,int k,int t){
        Map<Integer, Integer> buckets = new HashMap<>();
        int bucketSize = t+1;
        for (int i = 0; i < nums.length; i++) {
            int num = nums[i];
            //计算桶对应的id数
            int id = getBucketID(num,bucketSize);
            if (buckets.containsKey(id)
                    ||(buckets.containsKey(id-1)&&buckets.get(id-1)+t >= num)
                    ||(buckets.containsKey(id+1)&&buckets.get(id+1)-t <= num)){
                return true;
            }
            buckets.put(id,num);
            if (i >= k){
                buckets.remove(getBucketID(nums[i-k],bucketSize));
            }
        }
        return false;
    }

    private static int getBucketID(int num, int bucketSize) {
        return num>=0 ? num/bucketSize : (num+1)/bucketSize - 1;
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙崎流河

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值