题目: LCR 057.存在重复元素 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 * 10^4-2^31 <= nums[i] <= 2^31 - 10 <= k <= 1040 <= t <= 2^31 - 1
解答:
方法一:暴力法(超时)
遍历每个 i, j 的组合看是否符合题目条件,如果符合的话返回真值。主要就是注意一下溢出问题,因为nums[i]的范围较大,两数相减的绝对值会超出int的范围,所以为了避免溢出,应该在计算之前先将 nums[i] 和 nums[j] 转换为 long 类型。
class Solution {
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
int n=nums.length;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(Math.abs(i-j)<=k){
long r1 = Math.abs((long)nums[i]-(long)nums[j]);
if( r1 <= t)
return true;
}
}
}
return false;
}
}
方法二:滑动窗口 + TreeSet
对于序列中每一个元素 x 左侧的至多 k 个元素,如果这 k 个元素中存在一个元素落在区间 [x−t, x+t] 中,就找到了一对符合条件的元素。注意到对于两个相邻的元素,它们各自的左侧的 k 个元素中有 k−1 个是重合的。于是可以使用滑动窗口的思路,维护一个大小为 k 的滑动窗口,每次遍历到元素 x 时,滑动窗口中包含元素 x 前面的最多 k 个元素,检查窗口中是否存在元素落在区间 [x−t, x+t] 中即可。对于每个窗口中是否存在符合题意的值,只需比较其和 大于等于该值的最小值 的差值的绝对值和 小于等于该值的最大值 的差值的绝对值是否符合题意就可以了。
自己对 TreeSet的使用不是很了解,这里补充一下TreeSet的常用方法。
TreeSet的使用
TreeSet是Set集合中的一员,基于红黑树的数据结构实现,主要特点有:不重复,无索引,有序。
- 对于数值类型:
Integer、Double,默认按照从小到大的顺序进行排序。 - 对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序。
- 自定义排序规则:类本身实现
Comparable接口和创建TreeSet对象时传递比较器Comparator。
常用方法
- 获取元素
- first():获取最小的元素
- last():获取最大的元素
- floor(E e):获取小于或等于 e 的最大元素
- ceiling(E e):获取大于或等于 e 的最小元素
- higher(E e):获取大于 e 的最小元素
- lower(E e):获取小于 e 的最大元素
class Solution {
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
int n=nums.length;
TreeSet<Long> set=new TreeSet<>();
for(int i=0;i<n;i++){
// 转化为long类型防止溢出
long num=(long) nums[i];
//找到比num小的最大的数
Long floor=set.floor(num);
if(floor!=null && num-floor<=t){
return true;
}
//找到比num大的最小的数
Long ceiling=set.ceiling(num);
if(ceiling!=null && ceiling-num<=t){
return true;
}
set.add(num);
if(set.size()>k){
set.remove((long) nums[i-k]);
}
}
return false;
}
}