LeetCode.220 存在重复元素III(TreeSet妙用以及桶排序)

本文深入解析力扣第220题“存在重复元素III”,探讨在数组中寻找两个元素,其差的绝对值及下标差的绝对值分别满足特定条件的算法策略。重点介绍了两种高效解决方案:滑动窗口+TreeSet与桶排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

220. 存在重复元素 III去力扣刷题吧!

在整数数组 nums 中,是否存在两个下标 ij
–> ① nums[i]nums[j] 的差的绝对值小于等于 t
–> ② 且 ij 的差的绝对值也小于等于 k
在这里插入图片描述

问题分析

方法一. 滑动窗口 + TreeSet

  1. 由于在数组中需要满足两个下标的距离在k范围呢,因此可以结合滑动窗口的思想,只是这边的滑动窗口固定了窗口大小为k + 1,我们用一个TreeSet维护该窗口,当新加入元素num时,判断窗口中是否有元素在 [num - t, num + t]范围内,有则返回true,无则将num加入窗口,同时移出最左侧的元素。
  2. TreeSet的妙用:set.ceiling(num)Treeset中的上限函数,该方法可以返回一个set中大于等于num的最小值,在判断窗口中是否有元素在 [num - t, num + t]范围内若存在一个值在该范围内即可返回true
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
		//采用long是因为测试用例中有Integer越界问题
        TreeSet<Long> set = new TreeSet<>();
        int n = nums.length;
        for (int i = 0; i < n; i++) {
            if (i > k) {
                set.remove((long)nums[i - k - 1]);
            }
            // ceiling Treeset中的上限函数,set中大于某个值的最小值
            Long low = set.ceiling((long) nums[i] - t);
            //是否找到了符合条件的数
            if (low != null && low <= (long)nums[i] + t) {
                return true;
            }
            set.add((long) nums[i]);
        }
        return false;

方法二. 桶排序

  • 同时仍是基于滑动窗口的思想。
  • 我们将每个桶的大小设置为t + 1,每次判断当前num需要放进的桶编号中是否有值,或者相邻的桶中是否有元素在 [num - t, num + t]范围内。使用 map 存储,key 表示桶编号,value 表示桶中已存在的数字。
  • 同时在移动过程中需要将i - k - 1的值移出桶外,因为该值已经不会影响到当前num
  • 获取桶编号采用num / (t + 1)的方式,但是在数组中存在负数的情况,则会存在靠近0负数 / (t + 1) 时桶编号也为 0, 因此采用(num + 1) / (t + 1) - 1
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        if (t < 0) {
            return false;
        }
        HashMap<Long, Long> map = new HashMap<>();
        int n = nums.length;
        long w = t + 1; // 一个桶里边数字范围的个数是 t + 1
        for (int i = 0; i < n; i++) {
            //删除窗口中第一个数字
            if (i > k) {
                map.remove(getId(nums[i - k - 1], w));
            }
            //得到当前数的桶编号
            long id = getId(nums[i], w);
            //当前桶中已有值
            if (map.containsKey(id)) {
                return true;
            }
            //相邻的桶中有在[num - t, num + t]内
            if (map.containsKey(id + 1) && map.get(id + 1) - nums[i] < w) {
                return true;
            }
            if (map.containsKey(id - 1) && nums[i] - map.get(id - 1) < w) {
                return true;
            }
            map.put(id, (long) nums[i]);
        }
        return false;
    }

    private long getId(long num, long w) {
        if (num >= 0) {
            return num / w;
        } else {
            return (num + 1) / w - 1;
        }
    }

复杂度分析

  • 滑动窗口+TreeSet:O(nlog(k))
    TreeSetceiling方法的时间复杂度都是 O(log(n))
  • 桶排序:O(n)
    HashMap中取值是常数级的,同时我们只需要遍历一遍数组
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值