LeetCode | Contains Duplicate III
Given an array of integers, find out whether there are two distinct indices i and j in the array such that the difference between nums[i] and nums[j] is at most t and the difference between i and j is at most k.
要求:|nums[i]-nums[j]|<=t,且|i-j|<=k,用hashmap就没意义了,因为不是找相同,而是找相近了。
tag提示binary search tree,而java中的tree容器有:TreeSet与TreeMap。其中,TreeSet是基于TreeMap实现的,底层使用红黑树,属于平衡二叉树。TreeSet保证基本操作(add,remove,contains)时间复杂度为O(logn),且内部元素有序。(hashmap不保证有序,且两次返回的顺序也不一定相同,但是它对于get与put操作能保证constant-time performance)
TreeSet<E>中的一些方法:(注意TreeSet中的元素时有序的)
ceiling(E e):大于等于e中的最小值
floor(E e): 小于等于e中的最大值
higher(E e)/lower(E e):大于/小于 e中的 最小/最大 的元素
first(): 返回最小的元素
last(): 返回最大的元素
正确的代码:
public class Solution { public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { if(k<1 || t<0){ return false; } boolean result = false; TreeSet<Integer> mySet = new TreeSet<Integer>(); for(int i=0; i<nums.length; i++){ Integer high = mySet.ceiling(nums[i]); //大于等于nums[i]的最小值 Integer low = mySet.floor(nums[i]); //小于等于nums[i]的最大值 if(high!=null && high<=t+nums[i] || low!=null && nums[i]<=t+low){ result = true; break; } mySet.add(nums[i]); if(i>=k){ //使set的大小始终小于等于k,起到滑动窗口的作用 mySet.remove(nums[i-k]); } } return result; } }
注意,在上面的代码中: if(high!=null && high-nums[i]<=t || low!=null && nums[i]-low<=t){ result = true; break; } 这样的写法不行,leetcode有个testcase是[-1,2147483647], 1, 2147483647。按上面的写法会发生溢出。。。
下面是有错的代码:(用map< nums[i], index >来记录已遍历过的值及其index,这种想法是可行的,但在本题的条件下,就得额外考虑一些东西了)
public class Solution { public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { if(k<1 || t<0){ return false; } boolean result = false; TreeMap<Integer, Integer> myMap = new TreeMap<Integer, Integer>(); //找了很久,终于找到这段代码哪里错了。。。。 for(int i=0; i<nums.length; i++){ //[10,100,11,9],k=1,t=2,当循环到num[3]=9时,得low=null,high=<10,0> Integer high = myMap.ceilingKey(nums[i]); //而非<11,2>,代码判断t满足,但是k不满足,从而result=false... Integer low = myMap.floorKey(nums[i]); if(high!=null && high<=t+nums[i] && i-myMap.get(high)<=k){ result = true; //故在上面的情况下,仅仅找到一个high就判断当前遍历的nums[i]能否返回true是不够的 break; //还要找到比high大的最小元素再判断,然后循环找下去,甚至当对元素间距k要求很松时, } //可能得在map中找到所有比nums[i]大的元素判断 if(low!=null && nums[i]<=t+low && i-myMap.get(low)<=k){ result = true; break; } myMap.put(nums[i], i); } return result; } }