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;
}
}
我严重怀疑当初刷题的时候有没有理解这道题目。。。还是值得好好研究的,坑好像很多的样子。。。

浙公网安备 33010602011771号