每天一道编程题

53 阅读3分钟

题目: LCR 057.存在重复元素 III

给你一个整数数组 nums 和两个整数 kt 。请你判断是否存在 两个不同下标 ij,使得 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 - 1
  • 0 <= k <= 104
  • 0 <= 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集合中的一员,基于红黑树的数据结构实现,主要特点有:不重复,无索引,有序。

  • 对于数值类型:IntegerDouble,默认按照从小到大的顺序进行排序。
  • 对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序。
  • 自定义排序规则:类本身实现Comparable接口和创建TreeSet对象时传递比较器Comparator

常用方法

  1. 获取元素
  • 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;
    }
        
}