220 存在重复元素 III
难度:中等
语言:java
题目内容
给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。
如果存在则返回 true,不存在返回 false。
解题思路
首先肯定最好想到的办法就是暴力解法,两次遍历,首先选出满足条件的下标,再比较对应的值,这里就不多加赘述了,简单直接但不算好,最大的复杂度会到O(n2)。
那其实观察一下题目不难发现,它给了一个绝对值的概念,可以想到这是一个,以任意一个下标为中心,左右均为k长度的一个窗口,那窗口里面的值肯定是符合k的要求的,只要有一个值符合t的要求,就可以返回true。
题目就转化成了,在list一定范围内是否存在两值相减小于t的情况存在,但是直接遍历因为k的值如果够大的话,还是会导致,复杂度接近两次遍历,所以要使用一个比较简单的查找方法,就是解法中使用的树,TreeSet。
【注】 在 Java 中 TreeSet 是有序的去重集合(二叉搜索树,之前树的部份有涉及过,中序遍历是有序的),TreeMap 是 key 有序的哈希表,它们也是基于红黑树实现的。
【注】在TreeSet方法中
- floor(E e) 方法返回在这个集合中小于或者等于给定元素的最大元素,如果不存在这样的元素,返回null.
- ceiling(E e) 方法返回在这个集合中大于或者等于给定元素的最小元素,如果不存在这样的元素,返回null.
困惑
这个想法我是认可的,但是我在这个解法中有一个不懂的地方,写出来希望大家能帮我解答。解法是怎么cover到i+k的部分的,我可以理解每次结束删除nums[i-k]左边的元素,那这样不是意味着TreeSet里面存的永远只有i左边的k个数,而忽略了右边的K个数么?
class Solution {
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
int n = nums.length;
TreeSet<Long> ts = new TreeSet<>();
for (int i = 0; i < n; i++) {
Long u = nums[i] * 1L;
// 从 ts 中找到小于等于 u 的最大值(小于等于 u 的最接近 u 的数)
Long l = ts.floor(u);
// 从 ts 中找到大于等于 u 的最小值(大于等于 u 的最接近 u 的数)
Long r = ts.ceiling(u);
if(l != null && u - l <= t) return true;
if(r != null && r - u <= t) return true;
// 将当前数加到 ts 中,并移除下标范围不在 [max(0, i - k), i) 的数(维持滑动窗口大小为 k)
ts.add(u);
if (i >= k) ts.remove(nums[i - k] * 1L);
}
return false;
}
}