一、题目

给你一个整数数组 nums 和两个整数 k 和 t 。请你判断是否存在 两个不同下标 i 和 j,使得 abs(nums[i] - nums[j]) <= t ,同时又满足 abs(i - j) <= k 。

如果存在则返回 true,不存在返回 false。

点击查看原题

二、思路

该题思路很直观,就是维持一个数据数量为k的有序数据结构,根据新遍历到的数据num[i],来找到一个nums[j],使得abs(nums[i] - nums[j]) <= t 。解题方式就是这样,那么首先讨论有序数据结构:
1)有序数据结构需要不断插入和删除值
2)删除值不需要考虑重复元素的情况,因为重复元素一定符合题意返回true,删除之前就已经返回函数结果了

如何进行nums[j]的查找呢?我们需要找的是最靠近num[i]的两个值:即小于num[i]且最大、大于num[i]且最小。可以将这个化为在{num[i] - t, num[i] + t}中寻找值,那么,只需要找一个大于等于num[i] - t的最小整数,然后判断是否小于等于num[i] + t

三、代码

首先使用TreeSet作为有序数据结构,寻找元素是logn复杂度。

  1. class Solution {
  2. public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
  3. TreeSet<Long> treemap = new TreeSet();
  4. for (int i = 0; i < nums.length; i++) {
  5. Long ceiling = treemap.ceiling((long)nums[i] - (long)t); // 找一个大于等于num[i] - t的最小整数
  6. if (ceiling != null && ceiling <= (long)nums[i] + (long)t) { // 判断是否小于等于num[i] + t
  7. return true;
  8. }
  9. treemap.add((long)nums[i]);
  10. if (i >= k) {
  11. treemap.remove((long)nums[i - k]);
  12. }
  13. }
  14. return false;
  15. }
  16. }

四、桶思想优化

可将排序结构进一步优化,将所有元素进行分桶,桶大小为t+1,如果两个元素在同一个桶中,则一定符合题意要求返回true(这样一个桶中就一定只有一个元素,因为多个元素的情况直接排除了);如果隔壁桶有元素,那就取出元素进行加减法判断是否小于t即可。
根据上述思想,可以直接用一个hash表进行存储,这样取和拿的时间可以优化到O(1)。

  1. class Solution {
  2. public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
  3. Map<Integer, Long> map = new HashMap();
  4. for (int i = 0; i < nums.length; i++) {
  5. int indx = getIndx(nums[i], t);
  6. if (map.containsKey(indx)) {
  7. return true;
  8. }
  9. // 判断indx - 1桶中是否存在可行解
  10. if (map.containsKey(indx - 1) && (long)nums[i] - map.get(indx - 1) <= (long)t) {
  11. return true;
  12. }
  13. // 判断indx + 1桶中是否存在可行解
  14. if (map.containsKey(indx + 1) && map.get(indx + 1) - (long)nums[i] <= (long)t) {
  15. return true;
  16. }
  17. map.put(indx, (long)nums[i]);
  18. if (i >= k) { // 确保只有k个元素
  19. map.remove(getIndx(nums[i-k], t));
  20. }
  21. }
  22. return false;
  23. }
  24. private int getIndx(int val, int t) { // 桶大小为t+1,根据元素值算出属于哪个桶
  25. if (val < 0) { // 由于(-t, 0)和(0, t)在同一个桶中,为了避免,负值的桶下标整体减1
  26. return val/(t+1) - 1;
  27. }
  28. return val/(t+1);
  29. }
  30. }