剑指 Offer II 057. 值和下标之差都在给定的范围内(220. 存在重复元素 III)
题目:
思路:
【1】暴力破解的方式:
【2】桶计数+滑动窗口的方式:
利用桶排序的思想解决: 按照元素的大小进行分桶,维护一个滑动窗口内的元素对应的元素。 对于元素 xxx,其影响的区间为 [x−t,x+t]。于是可以设定桶的大小为 t+1。 如果两个元素同属一个桶,那么这两个元素必然符合条件。 如果两个元素属于相邻桶,那么我们需要校验这两个元素是否差值不超过 t。 如果两个元素既不属于同一个桶,也不属于相邻桶,那么这两个元素必然不符合条件。
【3】滑动窗口 + 有序集合的方式
代码展示:
桶计数+滑动窗口的方式:
//时间23 ms击败92.90% //内存44.1 MB击败15.9% //时间复杂度:O(n),其中 n 是给定数组的长度。 //每个元素至多被插入哈希表和从哈希表中删除一次,每次操作的时间复杂度均为 O(1)。 //空间复杂度:O(min(n,k)),其中 n 是给定数组的长度。哈希表中至多包含 min(n,k+1) 个元素。 class Solution { public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { int n = nums.length; Map<Long, Long> map = new HashMap<Long, Long>(); long w = (long) t + 1; for (int i = 0; i < n; 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)) < w) { return true; } if (map.containsKey(id + 1) && Math.abs(nums[i] - map.get(id + 1)) < w) { return true; } map.put(id, (long) nums[i]); if (i >= k) { map.remove(getID(nums[i - k], w)); } } return false; } //获取桶下标,假设w=5 public long getID(long x, long w) { // 如{0-4}算是下标为0的桶,{5-9}算是下标为1的桶 // 这些都是基于java 的整除是不留小数的 if (x >= 0) { return x / w; } // 由于0已经算是纳入下标为0的桶了,所以-1的桶应该要纳入{-1 - -5} // 故加一就是为了容纳满负数的桶,其次由于-1/5=0,也会算入到0的桶,故负数的桶的整体下标再左移一位 return ((x + 1) / w) - 1; } }
暴力破解的方式(遇事不决优先暴力):
//时间1398 ms击败7.93% //内存49.2 MB击败91.6% class Solution { public boolean containsNearbyAlmostDuplicate(int[] nums, int indexDiff, int valueDiff) { if (nums.length < 2) return false; for (int i = 0; i < nums.length; i++){ for (int j = i + indexDiff; j > i; j--){ if ( j >= nums.length) continue; if (Math.abs(nums[j] - nums[i]) <= valueDiff) return true; } } return false; } }