LeetCode Notes_#220_存在重复元素 III
LeetCode Notes_#220_存在重复元素 III
Contents
题目
在整数数组 nums 中,是否存在两个下标 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值小于等于 t ,且满足 i 和 j 的差的绝对值也小于等于 ķ 。
如果存在则返回 true,不存在返回 false。
示例 1:
输入: nums = [1,2,3,1], k = 3, t = 0
输出: true
示例 2:
输入: nums = [1,0,1,1], k = 1, t = 2
输出: true
示例 3:
输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false
思路分析
方法1:暴力搜索
最简单的思路是暴力搜索,两层循环
- 外层循环遍历数组中所有数字
nums[i]
- 内层循环遍历
nums[i+1...i+k]
范围内的所有数字,这是一个滑动窗口,计算nums[i]
与窗口内所有数字的差值,如果有任何一个数字与nums[i]
差值的绝对值不超过t,就返回true
。
但暴力搜索是无法通过的,leetcode
上专门设置了一个k==10000
的用例来使暴力搜索法超时。抖机灵的做法就是加上一句如下代码...这就是面向测试用例编程。
if(k==10000) return false;
开玩笑的,面试肯定不能这么写。
方法2:使用TreeSet(二叉搜索树)
使用java
当中的TreeSet
来保存数字。TreeSet
用二叉搜索树实现,可以利用TreeSet
里的ceiling
/floor
方法来找到窗口中是否有符合要求的值。详见代码。
解答
class Solution {
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
//TreeSet是特殊的Set,其中的元素不重复,且使用BST进行存储,可以快速查找ceiling,floor数
TreeSet<Long> set = new TreeSet<>();
for(int i = 0;i <= nums.length - 1;i++){
//c是比nums[i]大的最小数
Long c = set.ceiling((long)nums[i]);
//如果c不大于nums[i] + t,就直接返回true
//nums[i] + t可能超出int表示范围,所以要用long类型
if(c != null && c <= (long)nums[i] + (long)t) return true;
//s是比nums[i]小的最大数
Long f = set.floor((long)nums[i]);
//nums[i] - t可能超出int表示范围,所以要用long类型
if(f != null && f >= (long)nums[i] - (long)t) return true;
//将nums[i]加入set
set.add((long)nums[i]);
//窗口大小是k+1,当大小达到k+1时,就移除set当中窗口最左边的元素
if(set.size() >= k + 1){
set.remove((long)nums[i - k]);
}
}
return false;
}
}
复杂度分析
时间复杂度:O(nlog(min(n,k)))
,BST的搜索,插入,删除复杂度都是O(log(min(n,k)))
空间复杂度:O(min(n,k))
,set的大小是min(n,k)