LeetBook-查找表类算法

68 阅读14分钟

查找表类算法

查找表类算法 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台

两个数组的交集

解法1

  1. new两个hashset,第一个hashset存放第一个数组
  2. 第二个数组存放进第二个hashset,放入之前判断是否在第一个hashset中存在(hashset不可重复)
  3. 将第二个hashset放入数组返回
 class Solution {
     public int[] intersection(int[] nums1, int[] nums2) {
         Set<Integer> set1 = new HashSet<>();
         Set<Integer> set2 = new HashSet<>();
         for (Integer number : nums1) {
             set1.add(number);
         }
         for (Integer number : nums2) {
             if (set1.contains(number)) {
                 set2.add(number);
             }
         }
         int[] result = new int[set2.size()];
         int count = 0;
         for (Integer number : set2) {
             result[count] = number;
             count++;
         }
         return result;
     }
 }

快乐数

解法1

暴力循环

 class Solution {
     int count = 0;
     public boolean isHappy(int n) {
         int result = 0;
         for (int i = 0; i < 30; i++) {
             while (n != 0) {
                 int num = n % 10;
                 result += num * num;
                 n = n / 10;
             }
             if (result == 1) {
                 return true;
             }
             n = result;
             result = 0;
             count++;
         }
         return false;
     }
 }

解法2(思路)

解法1的优化,把每次循环出的结果放到一个hashset中,如果有重复就可以提前return,出现重复就形成循环了,不会快乐了

例子的问题,居然没有暴力循环快,这是没想到的

 class Solution {
     public boolean isHappy(int n) {
         HashSet<Integer> set = new HashSet<Integer>();
         while(n != 1){
             set.add(n);// 把每次得到 新数字插存入set中
             int tmp = n, sum = 0;// sum为新数字
             // 计算新数字
             while(tmp != 0){
                 sum += (tmp % 10) * (tmp % 10);
                 tmp /= 10;
             }
             if (set.contains(sum)) return false; // 新数字与原来的数字相等 会陷入死循环,即 不为快乐数字
             n = sum;
         }
         return true;
     }
 }

存在重复元素

解法1

  1. 使用HashSet存放遍历过的元素
  2. 如果碰到元素已经放入HashSet中,返回true,否则返回false
 class Solution {
     public boolean containsDuplicate(int[] nums) {
         Set<Integer> set = new HashSet<>();
         for (int i = 0; i < nums.length; i++) {
             if (set.contains(nums[i])) {
                 return true;
             }
             set.add(nums[i]);
         }
         return false;
     }
 }

解法2

解法1的基础上优化

  • 不使用contains方法,而用add方法返回值是否成功来进行判断
 class Solution {
     public boolean containsDuplicate(int[] nums) {
         Set<Integer> set = new HashSet<>();
         for (int i = 0; i < nums.length; i++) {
             if (!set.add(nums[i])) {
                 return true;
             }
         }
         return false;
     }
 }

两个数组的交集Ⅱ

解法1

暴力循环

 class Solution {
     public int[] intersect(int[] nums1, int[] nums2) {
         int index = 0;
         for (int i = 0; i < nums1.length; i++) {
             for (int j = 0; j < nums2.length; j++){
                 if (nums1[i] == nums2[j]) {
                     nums1[index] = nums1[i];
                     nums2[j] = -1;
                     index++;
                     break;
                 }
             }
         }
         int[] result = new int[index];
         for (int i = 0; i < index; i++) {
             result[i] = nums1[i];
         }
         return result;
     }
 }

解法2

  1. 新建一个数组
  2. 遍历list1,对于list1中的每个元素,都在新数组中对应下标自增1
  3. 新建一个result数组用于存放结果
  4. 遍历list2,对于list2中每个元素,对应新数组中的下标不为0的元素存放入result数组,并把新数组中元素自减1
  5. 用Arrays的copyOfRange方法返回结果效率较高
 class Solution {
     public int[] intersect(int[] nums1, int[] nums2) {
         int[] account = new int[1001];
         for (int i = 0; i < nums1.length; i++) {
             account[nums1[i]]++;
         }
         int[] result = new int[Math.min(nums1.length, nums2.length)];
         int index = 0;
         for (int i = 0; i < nums2.length; i++) {
             int val = nums2[i];
             if(account[val] > 0) {
                 account[val]--;
                 result[index] = val;
                 index++;
             }
         }
         return Arrays.copyOfRange(result, 0, index);
     }
 }

有效的字母异位词

解法1

  1. 使用HashMap,将第一个字符串对应的char放入HashMap,(k, v)=(元素,出现次数)
  2. 遍历第二个字符串对应的char数组,如果在map中存在就自减1
  3. 如果在map中不存在,或者存在的value为0,返回false
  4. 遍历结束返回true
 class Solution {
     public boolean isAnagram(String s, String t) {
         if (s.length() != t.length()) {
             return false;
         }
         Map<Character, Integer> map = new HashMap<>();
         char[] c1 = s.toCharArray();
         char[] c2 = t.toCharArray();
         for (int i = 0; i < c1.length; i++) {
             map.put(c1[i], map.getOrDefault(c1[i], 0) + 1);
         }
         for (int i = 0; i < c2.length; i++) {
             if (map.get(c2[i]) == null || map.get(c2[i]) == 0) {
                 return false;
             }
             map.put(c2[i], map.get(c2[i]) - 1);
         }
         return true;
     }
 }

解法2

  1. 转char数组
  2. 排序
  3. 返回equals判断Stirng.valueOf是否相等
 class Solution {
     public boolean isAnagram(String s, String t) {
         char[] c1 = s.toCharArray();
         char[] c2 = t.toCharArray();
         Arrays.sort(c1);
         Arrays.sort(c2);
         return String.valueOf(c1).equals(String.valueOf(c2));
     }
 }

同构字符串

解法1

  1. 用两个map存放两个字符串的互相映射
  2. 遍历字符串
  3. 如果遍历到某个元素存在映射但是和当前另一个元素不匹配则返回false
  4. 遍历结束则返回true
 class Solution {
     public boolean isIsomorphic(String s, String t) {
         Map<Character, Character> map = new HashMap<>();
         Map<Character, Character> map2 = new HashMap<>();
         char[] ss = s.toCharArray();
         char[] tt = t.toCharArray();
         for (int i = 0; i < ss.length; i++) {
             if (map.containsKey(ss[i])) {
                 if (map.get(ss[i]) != tt[i]) {
                     return false;
                 }
             }
             if (map2.containsKey(tt[i])) {
                 if (map2.get(tt[i]) != ss[i]) {
                     return false;
                 }
             }
             map.put(ss[i], tt[i]);
             map2.put(tt[i], ss[i]);
         }
         return true;
     }
 }

解法2

  • 用两个数组来做map进行优化
 class Solution {
     public boolean isIsomorphic(String s, String t) {
         char[] char_s = s.toCharArray();
         char[] char_t = t.toCharArray();
         HashMap<Character, Character> map = new HashMap<>();
         for (int i = 0; i < char_s.length; i++) {
             if (map.containsKey(char_s[i])) {
                 if (map.get(char_s[i]) != char_t[i]) {
                     return false;
                 }
             } else if (map.containsValue(char_t[i])) {
                 return false;
             } else {
                 map.put(char_s[i], char_t[i]);
             }
         } 
         return true;
     }
 }

根据字符出现频率排序

解法1

  • 桶排序

大佬的解法,注释很清楚

 class Solution {
     public String frequencySort(String s) {
         if (s.isEmpty() || s.length() == 1) {
             return s;
         }
 ​
         // 构造 HashMap。Key:s 中的每个元素;Value:对应元素出现的次数(即频率)
         Map<Character, Integer> store = new HashMap<>();
 ​
         for (char c : s.toCharArray()) {
             // 填充 HashMap。如果当前 Key c 不存在,getOrDefault() 方法返回默认值 0;
             // 否则返回当前 Key c 对应的 Value。
             // 不管哪种情况最后都在 0 或者 Value 的基础上 +1。
             store.put(c, store.getOrDefault(c, 0) + 1);
         }
 ​
         // 构造一个桶的集合(即一系列桶),桶的个数为 s 的长度 +1,因为 buckets[0] 没有意义
         // 目的是将出现频率为 i 的字符放到第 i 个桶里(即 buckets[i])
         List<Character>[] buckets = new List[s.length() + 1];
 ​
         for (char key : store.keySet()) {
             // 某个字符在 HashMap 中的 Value 是几就会被放到第几个桶里
             int value = store.get(key);
 ​
             if (buckets[value] == null) {
                 // 如果某个桶还未放入过字符(即未初始化),则初始化其为一个数组
                 buckets[value] = new ArrayList<Character>();
             }
 ​
             buckets[value].add(key); // 然后将字符放到桶中
         }
 ​
         StringBuilder res = new StringBuilder();
 ​
         for (int i = buckets.length - 1; i > 0; --i) {
             // 遍历每个桶
             if (buckets[i] != null) {
                 // 如果桶里有字符
                 for (char j : buckets[i]) {
                     // 遍历桶里的每个字符
                     for (int k = i; k > 0; --k) {
                         // 字符出现了几次就向 res 中添加几次该字符
                         res.append(j);
                     }
                 }
             }
         }
 ​
         return res.toString();
     }
 }

实现

 class Solution {
     public String frequencySort(String s) {
         if (s.isEmpty() || s.length() == 1) {
             return s;
         }
         Map<Character, Integer> map = new HashMap<>();
         List<Character>[] buckets = new List[s.length() + 1];
         char[] ccc = s.toCharArray();
         StringBuilder result = new StringBuilder();
         for (char c : ccc) {
             map.put(c, map.getOrDefault(c, 0) + 1);
         }
         for (char key : map.keySet()) {
             int value = map.get(key);
             if (buckets[value] == null) {
                 buckets[value] = new ArrayList<>();
             }
             buckets[value].add(key);
         }
         for (int i = buckets.length - 1; i > 0; i--) {
             if (buckets[i] == null) {
                 continue;
             }
             for (char c : buckets[i]) {
                 for (int j = 0; j < i; j++) {
                     result.append(c);
                 }
             }
         }
         return result.toString();
     }
 }

两数之和

解法1

暴力循环

 class Solution {
     public int[] twoSum(int[] nums, int target) {
         int[] result = new int[2];
         for (int i = 0; i < nums.length; i++) {
             for (int j = i + 1; j < nums.length; j++) {
                 if(nums[i] + nums[j] == target) {
                     result[0] = i;
                     result[1] = j;
                     return result;
                 }
             }
         }
         return result;
     }
 }

解法2

  1. 使用HashMap遍历一遍数组(map的key为元素值,value为元素下标)
  2. 每一次遍历都求出当前元素和target的差,如果该值在map中存在,则直接返回这两个值
  3. 否则将当前元素加入到map中
 class Solution {
     public int[] twoSum(int[] nums, int target) {
         int[] result = new int[2];
         Map<Integer, Integer> map = new HashMap<>();
         for (int i = 0; i < nums.length; i++) {
             int number = target - nums[i];
             if (map.containsKey(number)) {
                 result[0] = i;
                 result[1] = map.get(number);
                 return result;
             }
             map.put(nums[i], i);
         }
         return result;
     }
 }

解法3

在解法2的基础上优化一丢丢

 class Solution {
     public int[] twoSum(int[] nums, int target) {
         Map<Integer, Integer> map = new HashMap<>();
         for (int i = 0; i < nums.length; i++) {
             if (map.containsKey(target - nums[i])) {
                 return new int[]{i, map.get(target - nums[i])};
             }
             map.put(nums[i], i);
         }
         return new int[0];
     }
 }

三数之和

解法1

英雄哪里出来大佬的视频解法的Java实现

一层枚举加双指针

程序员是如何把自己逼疯的?哔哩哔哩bilibili

 class Solution {
     public List<List<Integer>> threeSum(int[] nums) {
         List<List<Integer>> result = new ArrayList<>();
         Arrays.sort(nums);
         int n = nums.length;
         int i, j, k;
         for (i = 0; i < n - 2; i++) {
             if (i != 0 && nums[i] == nums[i - 1]) {
                 continue;
             }
             j = i + 1;
             k = n - 1;
             while (j < k) {
                 if (nums[i] + nums[j] + nums[k] > 0) {
                     k--;
                 } else if (nums[i] + nums[j] + nums[k] < 0) {
                     j++;
                 } else {
                     List<Integer> re = new ArrayList<>();
                     re.add(nums[i]);
                     re.add(nums[j]);
                     re.add(nums[k]);
                     result.add(re);
                     j++;
                     k--;
                     while (j < k && nums[j] == nums[j - 1]) {
                         j++;
                     }
                     while (j < k && nums[k] == nums[k + 1]) {
                         k--;
                     }
                 }
             }
         }
         return result;
     }
 }

解法2

解法1优化,还是大佬的思路,太强了

 class Solution {
     public List<List<Integer>> threeSum(int[] nums) {
         List<List<Integer>> result = new ArrayList<>();
         Arrays.sort(nums);
         int n = nums.length;
         if (n < 3 || nums[0] > 0 || nums[n - 1] < 0) {
             return result;
         }
         if (nums[0] == 0 && nums[n - 1] == 0) {
             List<Integer> temp = new ArrayList<>();
             temp.add(0);
             temp.add(0);
             temp.add(0);
             result.add(temp);
             return result;
         }
         for (int i = 0; i < n - 2; i++) {
             if (i != 0 && nums[i] == nums[i - 1]) {
                 continue;
             }
             if (nums[i] > 0) {
                 break;
             }
             int j = i + 1;
             int k = n - 1;
             while (j < k) {
                 int sum = nums[i] + nums[j] + nums[k];
                 if (sum > 0) {
                     k--;
                 } else if (sum < 0) {
                     j++;
                 } else {
                     List<Integer> temp = new ArrayList<>();
                     temp.add(nums[i]);
                     temp.add(nums[j]);
                     temp.add(nums[k]);
                     result.add(temp);
                     j++;
                     k--;
                     while (j < k && nums[j] == nums[j - 1]) {
                         j++;
                     }
                     while (j < k && nums[k] == nums[k + 1]) {
                         k--;
                     }
                 }
             }
         }
         return result;
     }
 }

四数之和

解法1

英雄哪里出来大佬的视频解法的Java实现

两层枚举加双指针

《沉浸式编程》程序员的癫疯时刻哔哩哔哩bilibili

 class Solution {
     public List<List<Integer>> fourSum(int[] nums, int target) {
         List<List<Integer>> result = new ArrayList<>();
         Arrays.sort(nums);
         int n = nums.length;
         int i, j, k, l;
         if (n < 4) {
             return result;
         }
         Long sum4 = (long)(nums[0]) + (long)nums[1] + (long)nums[2] + (long)nums[3];
         for (i = 0; i < n; i++) {
             if (sum4 > target) {
                 break;
             }
             if (i > 0 && nums[i] == nums[i - 1]) {
                 continue;
             }
             for (j = i + 1; j < n; j++) {
                 if (j > i + 1 && nums[j] == nums[j - 1]) {
                     continue;
                 }
                 l = j + 1;
                 k = n - 1;
                 while (l < k) {
                     Long temp = (long)(nums[i]) + (long)nums[j] + (long)nums[l] + (long)nums[k];
                     if (temp > target) {
                         k--;
                     } else if (temp < target) {
                         l++;
                     } else {
                         List<Integer> re = new ArrayList<>();
                         re.add(nums[i]);
                         re.add(nums[j]);
                         re.add(nums[l]);
                         re.add(nums[k]);
                         result.add(re);
                         l++;
                         k--;
                         while (l < k && nums[l] == nums[l - 1]) {
                             l++;
                         }
                         while (l < k && nums[k] == nums[k + 1]) {
                             k--;
                         }
                     }
                 }
             }
         }
         return result;
     }
 }

解法2

跟着大佬的思路继续优化,思路看视频

 class Solution {
     public List<List<Integer>> fourSum(int[] nums, int target) {
         List<List<Integer>> result = new ArrayList<>();
         Arrays.sort(nums);
         int n = nums.length;
         int i, j, k, l;
         if (n < 4) {
             return result;
         }
         Long sum4 = (long)(nums[0]) + (long)nums[1] + (long)nums[2] + (long)nums[3];
         Long max3 = (long)(nums[n - 1]) + (long)nums[n - 2] + (long)nums[n - 3];
         Long max2 = (long)nums[n - 1] + nums[n - 2];
         for (i = 0; i < n - 3; i++) {
             if (sum4 > target) {
                 break;
             }
             if (i > 0 && nums[i] == nums[i - 1]) {
                 continue;
             }
             if (max3 + nums[i] < target) {
                 continue;
             }
             Long sum3 = (long)nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3];
             for (j = i + 1; j < n - 2; j++) {
                 if (sum3 > target) {
                     break;
                 }
                 if (j + 3 < n) {
                     sum3 = sum3 - nums[j] + nums[j + 3];
                 }
                 if (j > i + 1 && nums[j] == nums[j - 1]) {
                     continue;
                 }
                 if (max2 + nums[i] + nums[j] < target) {
                     continue;
                 }
                 l = j + 1;
                 k = n - 1;
                 while (l < k) {
                     Long temp = (long)(nums[i]) + (long)nums[j] + (long)nums[l] + (long)nums[k];
                     if (temp > target) {
                         k--;
                     } else if (temp < target) {
                         l++;
                     } else {
                         List<Integer> re = new ArrayList<>();
                         re.add(nums[i]);
                         re.add(nums[j]);
                         re.add(nums[l]);
                         re.add(nums[k]);
                         result.add(re);
                         l++;
                         k--;
                         while (l < k && nums[l] == nums[l - 1]) {
                             l++;
                         }
                         while (l < k && nums[k] == nums[k + 1]) {
                             k--;
                         }
                     }
                 }
             }
         }
         return result;
     }
 }

四数相加Ⅱ

解法1

  1. 四个数组分为两组
  2. 将AB的和及出现的次数放入到HashMap中
  3. 计算CD的和*(-1)如果在HashMap中出现则计数结果自增对应的value值
  4. 返回计数结果
 class Solution {
     public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
         int count = 0;
         Map<Integer, Integer> map = new HashMap<>();
         int sumAB;
         int sumCD;
         for (int i = 0; i < nums1.length; i++) {
             for (int j = 0; j < nums2.length; j++) {
                 sumAB = nums1[i] + nums2[j];
                 if (map.containsKey(sumAB)) {
                     map.put(sumAB, map.get(sumAB) + 1);
                     continue;
                 }
                 map.put(sumAB, 1);
             }
         }
         for (int i = 0; i < nums3.length; i++) {
             for (int j = 0; j < nums4.length; j++) {
                 sumCD = (nums3[i] + nums4[j]) * (-1);
                 if (map.containsKey(sumCD)) {
                     count += map.get(sumCD);
                 }
             }
         }
         return count;
     }
 }

字母异位词分组

解法1

  1. 将每个字符串排序后放入一个新的字符串数组,与原数组对应
  2. 遍历原数组
  3. 如果对应的排序后的数组对应值存在在map中,则将原数组的元素添加到map的value(List)里
  4. 如果不存在,则将原数组元素作为key,新数组元素作为value(放入list),put进map
  5. 遍历map取出所有value放入result(List)返回
 class Solution {
     public List<List<String>> groupAnagrams(String[] strs) {
         String[] newstr = new String[strs.length];
         List<List<String>> result = new ArrayList<>();
         Map<String, List<String>> map = new HashMap<>();
         for (int i = 0; i < strs.length; i++) {
             char[] ss = strs[i].toCharArray();
             Arrays.sort(ss);
             newstr[i] = String.valueOf(ss);
         }
         for (int i = 0; i < strs.length; i++) {
             if (map.containsKey(newstr[i])) {
                 map.get(newstr[i]).add(strs[i]);
                 continue;
             }
             List<String> list = new ArrayList<>();
             list.add(strs[i]);
             map.put(newstr[i], list);
         }
         // Iterator iterator = map.keySet().iterator();
         for (Map.Entry<String, List<String>> entry : map.entrySet()) {
             result.add(entry.getValue());
         }
         return result;
     }
 }

解法2

  1. 遍历字符串数组
  2. 将元素排序,并在map中get元素的value
  3. 如果value为null,则将字符串和对应的ArrayList(空的)放入map中,并将ArrayList放入result中
  4. 向ArrayList中添加元素
  5. 返回result
 class Solution {
     public List<List<String>> groupAnagrams(String[] strs) {
         List<List<String>> result = new ArrayList<>();
         Map<String, ArrayList<String>> map = new HashMap<>();
         char[] ccc;
         String sss;
         ArrayList<String> temp = null;
         for (String str : strs) {
             ccc = str.toCharArray();
             Arrays.sort(ccc);
             sss = String.valueOf(ccc);
             temp = map.get(sss);
             if (temp == null) {
                 temp = new ArrayList<>();
                 map.put(sss, temp);
                 result.add(temp);
             }
             temp.add(str);
         }
         return result;
     }
 }

回旋镖的数量

解法1

  1. 遍历所有节点
  2. 将当前节点和其他节点距离作为key,出现距离的次数作为value存入map
  3. 遍历map,result自加每个节点的value*(value-1)
  4. 返回result
 class Solution {
     public int numberOfBoomerangs(int[][] points) {
         int result = 0;
         Map<Integer, Integer> map = null;
         for (int[] m : points) {
             map = new HashMap<>();
             for (int[] n : points) {
                 int len = (m[0] - n[0]) * (m[0] - n[0]) + (m[1] - n[1]) * (m[1] - n[1]);
                 map.put(len, map.getOrDefault(len, 0) + 1);
             }
             for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
                 result += entry.getValue() * (entry.getValue() - 1);
             }
         }
         return result;
     }
 }

直线上最多的点数

解法1

  1. 遍历数组所有点
  2. 针对每个点再次遍历数组,计算出当前点和所有点的斜率,将斜率(key)和出现次数(value)放入map
  3. 遍历map,将value和max中的最大值放入max,继续下一次遍历
  4. 返回max
 class Solution {
     public int maxPoints(int[][] points) {
         int max = 1;
         Map<Double, Integer> map = null;
         for (int[] point : points) {
             map = new HashMap<>();
             for (int[] p : points) {
                 if (p == point) {
                     continue;
                 }
                 double k = Integer.MAX_VALUE;
                 if (point[0] - p[0] != 0) {
                     k = ((double)p[1] - point[1]) / (p[0] - point[0]);
                 }
                 map.put(k, map.getOrDefault(k, 0) + 1);
             }
             for (int value : map.values()) {
                 max = Math.max(max, value + 1);
             }
         }
         return max;
     }
 }

存在重复元素Ⅱ

解法1

 class Solution {
     public boolean containsNearbyDuplicate(int[] nums, int k) {
         Map<Integer, Integer> map = new HashMap<>();
         for (int i = 0; i < nums.length; i++) {
             if (!map.containsKey(nums[i])) {
                 map.put(nums[i], i);
                 continue;
             }
             if (i - map.get(nums[i]) <= k) {
                 return true;
             }
             map.put(nums[i], i);
         }
         return false;
     }
 }

存在重复元素Ⅲ

【宫水三叶】一题双解:「滑动窗口 & 二分」&「桶排序」解法 - 存在重复元素 III - 力扣(LeetCode)

解法1

桶排序

 class Solution {
     long size;
     public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
         int n = nums.length;
         Map<Long, Long> map = new HashMap<>();
         size = t + 1L;
         for (int i = 0; i < n; i++) {
             long u = nums[i] * 1L;
             long idx = getIdx(u);
             // 目标桶已存在(桶不为空),说明前面已有 [u - t, u + t] 范围的数字
             if (map.containsKey(idx)) return true;
             // 检查相邻的桶
             long l = idx - 1, r = idx + 1;
             if (map.containsKey(l) && u - map.get(l) <= t) return true;
             if (map.containsKey(r) && map.get(r) - u <= t) return true;
             // 建立目标桶
             map.put(idx, u);
             // 移除下标范围不在 [max(0, i - k), i) 内的桶
             if (i >= k) map.remove(getIdx(nums[i - k] * 1L));
         }
         return false;
     }
     long getIdx(long u) {
         return u >= 0 ? u / size : ((u + 1) / size) - 1;
     }
 }

解法2

滑动窗口,二分

 class Solution {
     public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
         int n = nums.length;
         TreeSet<Long> ts = new TreeSet<>();
         for (int i = 0; i < n; i++) {
             Long u = nums[i] * 1L;
             // 从 ts 中找到小于等于 u 的最大值(小于等于 u 的最接近 u 的数)
             Long l = ts.floor(u); 
             // 从 ts 中找到大于等于 u 的最小值(大于等于 u 的最接近 u 的数)
             Long r = ts.ceiling(u); 
             if(l != null && u - l <= t) return true;
             if(r != null && r - u <= t) return true;
             // 将当前数加到 ts 中,并移除下标范围不在 [max(0, i - k), i) 的数(维持滑动窗口大小为 k)
             ts.add(u);
             if (i >= k) ts.remove(nums[i - k] * 1L);
         }
         return false;
     }
 }

解法3

评论区找到的大佬用二叉搜索树+TreeSet+桶排序解决的

 /**
  * @author miemiehoho
  * @date 2021/11/10 11:14
  */
 public     // 利用二叉搜索树维护长度为k的滑动窗口内的数
 class Solution {
     class TreeNode {
         long val;
         TreeNode left;
         TreeNode right;
 ​
         TreeNode() {
         }
 ​
         TreeNode(long val) {
             this.val = val;
         }
 ​
         TreeNode(long val, TreeNode left, TreeNode right) {
             this.val = val;
             this.left = left;
             this.right = right;
         }
     }
 ​
     TreeNode root;
     int size = 0;
 ​
     // 查找的时间复杂度为O(h)
     public boolean searchBST(TreeNode root, long minVal, long maxVal) {
         if (root == null) {
             return false;
         }
         if (root.val >= minVal && root.val <= maxVal) {
             return true;
         }
         if (root.val > maxVal) {
             return searchBST(root.left, minVal, maxVal);
         }
         return searchBST(root.right, minVal, maxVal);
     }
 ​
     public void insert(TreeNode root, long val) {
         this.root = insertIntoBST(root, val);
     }
 ​
     // 插入的时间复杂度为O(N),其中 N 为树中节点的数目。最坏情况下,我们需要将值插入到树的最深的叶子结点上,而叶子节点最深为 O(N)
     private TreeNode insertIntoBST(TreeNode root, long val) {
         if (root == null) {
             size++;
             return new TreeNode(val);
         }
         if (root.val > val) {
             root.left = insertIntoBST(root.left, val);
         } else {
             root.right = insertIntoBST(root.right, val);
         }
         return root;
     }
 ​
     public void delete(TreeNode root, long key) {
         this.root = deleteNode(root, key);
     }
 ​
     // 删除操作的时间复杂度为O(h),h为树的高度
     private TreeNode deleteNode(TreeNode root, long key) {
         if (root == null) {
             return null;
         }
         if (root.val > key) {
             root.left = deleteNode(root.left, key);
         }
         if (root.val < key) {
             root.right = deleteNode(root.right, key);
         }
         if (root.val == key) {
             if (root.left == null) {
                 return root.right;
             }
             if (root.right == null) {
                 return root.left;
             }
             TreeNode minNode = minNode(root.right);
             root.val = minNode.val;
             size--;
             root.right = deleteNode(root.right, root.val);
         }
         return root;
     }
 ​
     private TreeNode minNode(TreeNode root) {
         if (root == null) {
             return root;
         }
         while (root.left != null) {
             root = root.left;
         }
         return root;
     }
 //        // 删除操作的时间复杂度为 O(h),h 为树的高度
 //        public TreeNode deleteNode(TreeNode root, int key) {
 //            if (root == null) {
 //                return root;
 //            }
 //            if (root.val > key) {
 //                root.left = deleteNode(root.left, key);
 //            }
 //            if (root.val < key) {
 //                root.right = deleteNode(root.right, key);
 //            }
 //            if (root.val == key) {
 //                root = newTree(root);
 //            }
 //            return root;
 //        }
 //
 //        private TreeNode newTree(TreeNode root) {
 //            if (root.left == null) {
 //                return root.right;
 //            }
 //            if (root.right == null) {
 //                return root.left;
 //            }
 //            // 找到右子树最小值
 //            TreeNode cur = root.right;
 //            while (cur.left != null) {
 //                cur = cur.left;
 //            }
 //            cur.left = root.left;
 //            return root.right;
 //        }
 ​
     // 利用 BTS维护大小为k的滑动窗口
     public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
         if (nums.length <= 1) {
             return false;
         }
         for (int i = 0; i < nums.length; i++) {
             long minVal = (long)nums[i] - t;
             long maxVal = (long)nums[i] + t;
             if (searchBST(root, minVal, maxVal)) {
                 return true;
             }
             insert(root, nums[i]);
             if (size > k) {
                 delete(root, nums[i - k]);
             }
         }
         return false;
     }
 }