Contains Duplicate III Leetcode
Given an array of integers, find out whether there are two distinct indices i and j in the array such that the absolute difference between nums[i] and nums[j] is at most t and the absolute difference between i and j is at most k.
这道题做的还真的不容易。。。不过也算部分了解bucket sort了吧,只是还不熟练以后需要回顾。
主要思想是把所有的数分成t + 1个bucket,用num[i] / (t + 1)就可以得出这个数应该所在的bucket,放进去。这样距离为t的数字肯定在同个bucket或者相邻的bucket。
当hashmap的size大于等于k的时候,说明index距离已经大于k,这个时候要remove最原始的数。
注意:
1.相邻的bucket不一定就相距t所以要检查,所以value不可以放index要放resize之后的数。
2.bucket.size() >= k要有等号,因为这个if在前一个if后面,数字已经判断过,所以包含等号,一定要注意一下。
3.t<0 false, k <= 0 false;
4.注意边界溢出的问题。
public class Solution { public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { if (nums == null || nums.length == 0 || t < 0 || k < 1) { return false; } Map<Long, Long> bucket = new HashMap<>(); for (int i = 0; i < nums.length; i++) { long resize = (long) nums[i] - Integer.MIN_VALUE; long residual = resize / ((long) t + 1); if (bucket.containsKey(residual) || (bucket.containsKey(residual + 1) && bucket.get(residual + 1) - resize <= t || (bucket.containsKey(residual - 1) && resize - bucket.get(residual - 1) <= t))) { return true; } if (bucket.size() >= k) { long last = (long) nums[i - k] - Integer.MIN_VALUE; long r = last / ((long) t + 1); bucket.remove(r); } bucket.put(residual, resize); } return false; } }
这个题还可以用treeset做,treeset的话时间复杂度是O(nlgk)因为treeset add, remove,查找都是logn的时间复杂度。
again,注意越界。
public class Solution { public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { if (nums == null || nums.length == 0 || t < 0 || k < 1) { return false; } TreeSet<Long> ts = new TreeSet<>(); for (int i = 0; i < nums.length; i++) { Long floor = ts.floor((long) nums[i]); Long ceil = ts.ceiling((long) nums[i]); if ((floor != null && nums[i] - floor <= t) || (ceil != null && ceil - nums[i] <= t)) { return true; } if (i >= k) { ts.remove((long) nums[i - k]); } ts.add((long)nums[i]); } return false; } }
时隔几个月又来做这道题还是做不利索,而且做复杂了。。。但是这样是可以work的。
public class Solution { public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { if (k < 0 || t < 0 || nums == null) { return false; } Map<Long, Map<Integer, Integer>> hm = new HashMap<>(); for (int i = 0; i < nums.length; i++) { long d = nums[i] / (t + 1); Map<Integer, Integer> temp = hm.getOrDefault(d, new HashMap<>()); if (!hm.containsKey(d)) { hm.put(d, temp); } if (checkExist(hm.getOrDefault(d, null), k, t, nums, i) || checkExist(hm.getOrDefault(d - 1, null), k, t, nums, i) || checkExist(hm.getOrDefault(d + 1, null), k, t, nums, i)) { return true; } temp.put(nums[i], i); } return false; } private boolean checkExist(Map<Integer, Integer> hm, int k, int t, int[] nums, int i) { if (hm == null) { return false; } for (int key : hm.keySet()) { long trans = (long) key; long v = (long) nums[i]; if (Math.abs(trans - v) <= t && i - hm.get(key) <= k) { return true; } } return false; } }
其实没必要在hashmap里面存list,存最近的就可以了,和之前的一样。而且一个bucket里面的肯定符合要求,就不用check了。直接除以(t+1)的话,落在同一个bucket的数字,余数是从0到t,他们之间的差最多是t,所以不用检查。但有一点例外,就是有的时候负数和正数除以一个数可能落在相同的bucket但是相差的很多。比如-3,3,t = 4的时候。这个时候要么检查一下想减是不是小于等于t,要么就把他们都变成正数。
经实验证明都变成正数比较快。
还有一个教训是,只要涉及到减法加法之类的过程,哪怕是加一,也得考虑边界溢出。。。
public class Solution { public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { if (k < 1 || t < 0 || nums == null) { return false; } Map<Long, Long> hm = new HashMap<>(); for (int i = 0; i < nums.length; i++) { long resize = (long) nums[i] - Integer.MIN_VALUE; long n = resize / ((long) t + 1); if (hm.containsKey(n) || (hm.containsKey(n - 1) && Math.abs(hm.get(n - 1) - nums[i]) <= t) || (hm.containsKey(n + 1) && Math.abs(hm.get(n + 1) - nums[i]) <= t)) { return true; } else { hm.put(n, (long) nums[i]); } if (hm.size() > k) { hm.remove(((long) nums[i - k] - Integer.MIN_VALUE) / ((long)t + 1)); } } return false; } }
我严重怀疑当初刷题的时候有没有理解这道题目。。。还是值得好好研究的,坑好像很多的样子。。。