一刷242-二分模块- Offer II 057. 值和下标之差都在给定的范围内(同:220. 存在重复元素 III)

题目:
给你一个整数数组 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
-----------------------
思路:滑动窗口 + 二分 (红黑树)
此题容易让人联想到滑动窗口法,本文也是基于滑动窗口法,
只不过在滑动窗口法的基础上加了个红黑树的应用,核心思路如下:

遍历数组(i 指向当前元素),寻找 [i-k,i) (i-k >= 0) 中 最接近nums[i] 的两个数:
floor : 窗口 [i-k,i) 中, 比nums[i]小的数中最接近nums[i]的数。
ceiling : 窗口 [i-k,i) 中, 比nums[i]大的数中最接近nums[i]的数。
如果窗口中最接近nums[i]的数与nums[i]的差 <= t, 则满足条件, 返回 true,
否则移动窗口,继续遍历下一个元素。

如何高效地在窗口中找到最接近nums[i]的数呢?

直接遍历,效率太低,这时候我们会想到使用二分查找,在窗口中进行查找。
那我们用数组对窗口的值进行保存吗?不行,因为我们滑动窗口的时候,要将窗口左边的元素删除,
删除操作需要移动数据,复杂度高。
查找,插入,删除操作效率都要高,这时候我们可以使用红黑树,
红黑树的插入、删除、查找时间复杂度都为O(logn)Java中的 TreeSet 就是使用红黑树实现的,且可以轻松地获取到元素的 floor 和 ceiling。

红黑树:TreeSet  TreeSet<Integer> numbers = new TreeSet<>();
floor(element) - 返回小于指定元素(element)的元素中最大的元素。
		如果传递的元素(element)存在于树集中,则返回作为参数传递的元素(element)ceiling(element) - 返回大于指定元素(element)的那些元素中的最小元素。
		如果传递的元素(element)存在于树集中,则返回作为参数传递的元素(element)class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        if (nums.length < 2 || k == 0) return false;//特判
        TreeSet<Long> treeSet = new TreeSet<>();//红黑树: 保存长度为k的窗口内的数
        for (int i = 0; i < nums.length; i++) {
            Long cur = nums[i] * 1L;
            Long floor = treeSet.floor(cur);//获取比cur小的数中最接近cur的数!! Long
            Long ceiling = treeSet.ceiling(cur);//获取比cur大的数中最接近cur的数
            if (floor != null && cur - floor <= t) return true;//!! floor != null
            if (ceiling != null && ceiling - cur <= t) return true;
            treeSet.add(cur);//将当前数添加到窗口中
            if (i >= k) treeSet.remove(nums[i - k] * 1L);//剔除窗口最右边的元素
        }//!!  treeSet.remove()
        return false;
    }
}
/**
由于 cur - floor 可能会出现溢出的情况, 所以用Long来表示

if (floor != null && cur - floor <= t) return true;
if (ceiling != null && ceiling - cur <= t) return true;
如果窗口中最接近 cur 的数 与 cur 的差 <= t
说明存在这样的两个数,返回true

Long.valueOf(参数)是将参数转换成long的包装类——Long;也就是把基本数据类型转换成包装类
 */
时间复杂度:TreeSet 基于红黑树,查找和插入都是O(logk) 复杂度。整体复杂度为 O(nlogk)
空间复杂度:O(k)

LC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值