Problem:
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.
Analyse:
In this problem, we can come up with three methods.
brute force
Obviously, scanning the array with two “for loops” is the most direct method. However, the time complexity will be O(kn) and.the space complexity is O(1), which seems not much efficient.
treeset --> balance tree
In this method, we still need to scan the array one time and store the number into the treeset with the size of k. Luckily, we can get the ceiling and floor of the num by using the property of tree set.
time complexity is O(n)
space complexity is O(nlogk)
bucket --> hashmap
Assuming the length of one bucket is t, any two numbers’ difference in one bucket will smaller than t. So, we would like to build k buckets and put the numbers into the bucket. Then we only need to check each left and right neighbour buckets and itself’s value.
time comlexity is O(n)
space complexity is O(k)
public class Solution1 {
// brute force
public boolean containsNearbyAlmostDuplicate1(int[] nums, int k, int t) {
// corner case 一般要对input都进行讨论
// k is absolute difference between i and j (不会有任何pair的index的差值等于0)
if (nums.length == 0 || nums == null || k <= 0 || t < 0) {
return false;
}
for (int i=0; i<nums.length; i++) {
for (int j=i+1; j-i<=k && j<nums.length; j++) {
if (Math.abs(Long.valueOf(nums[j]) - Long.valueOf(nums[i])) <= t) return true;
}
}
return false;
}
// treeset --> balance tree
public boolean containsNearbyAlmostDuplicate2(int[] nums, int k, int t) {
if (nums.length == 0 || nums == null || k <= 0 || t < 0) {
return false;
}
TreeSet<Integer> treeSet = new TreeSet<>();
for (int i=0; i<nums.length; i++) {
Integer higherNum = treeSet.ceiling(nums[i]);
if (higherNum != null && Long.valueOf(higherNum) - Long.valueOf(nums[i]) <= t) {
return true;
}
Integer lowerNum = treeSet.floor(nums[i]);
if (lowerNum != null && Long.valueOf(nums[i]) - Long.valueOf(lowerNum) <= t) {
return true;
}
treeSet.add(nums[i]);
if (i >= k) {
treeSet.remove(nums[i-k]);
}
}
return false;
}
// bucket --> hashmap
public boolean containsNearbyAlmostDuplicate3(int[] nums, int k, int t) {
if (nums.length == 0 || nums == null || k <= 0 || t < 0 ) return false;
int min = Integer.MAX_VALUE;
for (int num:nums) {
min = Math.min(min, num);
}
long diff = Long.valueOf(t) + 1;
HashMap<Long, Integer> bucket = new HashMap<>();
for (int i=0; i<nums.length; i++) {
Long index = ( Long.valueOf(nums[i]) - Long.valueOf(min) ) / diff;
// check the left bucket
Integer bucket_left = bucket.get(index - 1);
if (bucket_left != null && Long.valueOf(nums[i]) - Long.valueOf(bucket_left) <= t) {
return true;
}
// check the right bucket
Integer bucket_right = bucket.get(index + 1);
if (bucket_right != null && Long.valueOf(bucket_right) - Long.valueOf(nums[i]) <= t) {
return true;
}
// check itself
Integer bucket_value = bucket.get(index);
if (bucket_value != null) {
return true;
}
bucket.put(index, nums[i]);
// we do this step is because we want to reduce the space complexity form O(#buckets) to O(k)
if (i >= k) {
bucket.remove(( Long.valueOf(nums[i - k]) - Long.valueOf(min) ) / diff);
}
}
return false;
}
}