【leetcode】值和下标之差都在给定的范围内

一、代码描述

给你一个整数数组 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

二、代码思路

本题可以理解为:给定元素 i 在以其为中心两边各为K大小的滑动窗口中,是否找到一个元素j,使得 i - t <= j <= i + t。

如果我们使用队列来描述滑动窗口,则由于队列是无序的所以找元素花的时间为o(n),所以遍历完整个列表时间复杂度为o(n2),显然超时。

所以我们考虑其他数据结构:

  • 能够插入删除指定元素。
  • 保证插入的元素能够排好序,并使用二分快速找到。
  • 所以我们就选定了TreeSet数据结构,其保证增序且唯一。
  • 时间复杂度为o(nlogn)

本题可以转换为:在下标j的前K个元素中,有没有符合abs(nums[i] - nums[j]) <= t的情况

上述的公式也可以转换为(把绝对值去掉):-t <= numsi - numsj <= t
进一步转换:numsi - t <= numsj <= numsi + t。

所以,是不是有这样一种想法:把j的前k个元素放到一个数据结构中,然后我们只需要从中取出来一个大于numsi的最小元素,然后判断是否符合上述两个式子即可。

为什么我们只需要从中取出来一个大于numsi - t 的最小元素呢?

因为 t是正的,所以只有取出numsj越小,在给定的numsi的上述不等式中 numsj才能符合。

同时我们还要保证加入集合中的元素的下标是符合abs(i - j) <= k的也就是: i j 都是正的,i > k 的时候,需要把 小于 i - k的元素都删掉,i < k的时候,由于i是从i = 0,逐步递增的。

所以,就算只维护左侧的k的窗口也是可以的,因为由于绝对值的特殊性,当numi 和 numj互换也没问题,就是说num[i] - nums[j] 就算交换相减也没问题,所以并没有丢失右侧的numj。

三、代码思路
class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        //本题可以转换为:在下标j的前K个元素中,有没有符合abs(nums[i] - nums[j]) <= t的情况
        //上述的公式也可以转换为(把绝对值去掉):-t <= numsi - numsj <= t 
        //进一步转换:numsi - t <= numsj <= numsi + t
        //所以,是不是有这样一种想法:把j的前k个元素放到一个数据结构中,然后我们只需要从中取出来一个大于numsi的最小元素
        //然后判断是否符合上述两个式子即可。
        //为什么我们只需要从中取出来一个大于numsi - t 的最小元素呢?
        //因为 t是正的,所以只有取出numsj越小,在给定的numsi的上述不等式中 numsj才能符合。
        //同时我们还要保证加入集合中的元素的下标是符合abs(i - j) <= k的
        //也就是: i j 都是正的,i > k 的时候,需要把 小于 i - k的元素都删掉,i < k的时候,由于i是从i = 0,逐步递增的
        //所以,就算只维护左侧的k的窗口也是可以的,因为由于绝对值的特殊性,当numi 和 numj互换也没问题。
        int n = nums.length;
        TreeSet<Long> set = new TreeSet();
        for (int i = 0; i < n; i++) {
            Long ceil = set.ceiling((long) nums[i] - (long) t);
            if (ceil != null && ceil <= (long) nums[i] + (long) t) {
                return true;
            }
            set.add((long) nums[i]);
            if (i >= k) {
                set.remove((long) nums[i - k]);
            }
        }
        return false;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值