剑指Offer || 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 * 104
  • -231 <= nums[i] <= 231 - 1
  • 0 <= k <= 104
  • 0 <= t <= 231 - 1

注意:本题与主站 220 题相同: 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

LCR 057. 存在重复元素 III - 力扣(LeetCode)

题解

思路一:暴力,看x的前k个元素有没有满足条件的,挨个判断。复杂度太高

思路二:题目可以转化为:在当前元素nums[i]下,寻找前k个元素中有没有元素使得其在区间[nums[i]-t,nums[i]+t]之间。这需要一个数据结构:1.支持新增和删除元素,维护滑动窗口。2.内部元素有序,可以判断是否有区间内元素。有序集合TreeSet,内部由红黑树实现。

代码:

class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
    	TreeSet<Long> set=new TreeSet<Long>();
    	for(int i=0;i<nums.length;i++) {
    		Long tmp=set.ceiling((long)nums[i]-t);
    		if(tmp!=null&&tmp<=(long)nums[i]+(long)t) return true;
    		set.add((long)nums[i]);
    		if(i>=k) set.remove((long)nums[i-k]);
    	}
    	return false;
    }
}

tips:一般写List=new ArrayList,即抽象而不是实现,后续无论是array还是linked,都可以解耦修改。但是当需要用到ArrayList特有的方法时就只能写ArrayList=。例如本题目中的ceiling,floor方法

思路三:桶排序,每t+1个元素放在一个桶中,这样桶中差值最大即为t(例如0,1,2最大差值为2,t=2但总数=3).然后当前元素x,如果k内有同桶元素即true,若没有则查看相邻桶再做判断。因为区间为[x-t,x+t],因此最大最小可能绝对不会出相邻桶。关于分桶,x>0,[0,t]=0(特殊情况,不套n),[t+1,2t+1]=1,[(n)t+1,(n+1)t+1]=n。x<0,[-t,-1]=-1,[-t-1,-2t-1]=-2,[-(n+1)t-1,-(n)t-1]=-n.所以x>0时桶号n=x/t+1=x/w。x<0时,桶号n=x+1/t=x+1/w-1。除法会自动向上取整,正负数的上不一样,即向0靠近。

用map<Long,Long>表示<桶号,桶>因为一个桶有两个元素就会返回true,因此一个桶只有一个元素,可以用哈希表的一条key-value表示桶

为什么只留一个值可以确保?比如桶[1][3],[1][4]当桶里只有一个元素时,t=2,[0,1,2][3,4,5].查1时3满足,4不满足.若nums没有3或13距离过远,则不考虑;若有3且满足下标差<k,这时14判断false,但是遍历到3时会和4true。如果想不明白,map的value存一个collection也可以。

下标的话,只保留x之前k个元素的值在桶里,因此只要桶里有,就true

代码:

class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        long w = (long) t + 1;
        Map<Long, Long> map = new HashMap<Long, Long>();
        for (int i = 0; i < nums.length; i++) {
            long id = getID(nums[i], w);
            if (map.containsKey(id))  return true;
            if (map.containsKey(id - 1) && Math.abs(nums[i] - map.get(id - 1)) <= t)  return true;
            if (map.containsKey(id + 1) && Math.abs(nums[i] - map.get(id + 1)) <= t)  return true;
            map.put(id, (long) nums[i]);
            if (i >= k)  map.remove(getID(nums[i - k], w));
        }
        return false;
    }
    public long getID(long x, long w) {
        if (x >= 0)  return x / w;
        else return (x + 1) / w - 1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值