给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。
示例 1:
输入: nums = [1,2,3,1], k = 3
输出: true
示例 2:
输入: nums = [1,0,1,1], k = 1
输出: true
示例 3:
输入: nums = [1,2,3,1,2,3], k = 2
输出: false
O(N^2)解法 暴力破解
首先最容易想到的解法就是用O(n^2)复杂度暴力解法
两个指针,第一个指针是定在前面,第二个指针后移,
大概思路如我画的图所示
通过第一个指针的index和第二个指针index不断的转移,最终找到j-i<k并且两个所指的数字相等的结果返回布尔值。
public boolean containsNearbyDuplicate(int[] nums, int k) {
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] == nums[j] && j - i <= k) {
return true;
}
}
}
return false;
}
O(N)解法(1)
我并不满足与On^2这个解法,因为效率很低,考虑了一下可以用Map记录这个val的位置具体示意图如下
实例化一个Map相当于一个字典(类型Map(Integer,Integer))用当前指针索引指向的值做key,用当前索引的位置做val,假如key在这个Map中存在,就用当前这个指针的位置与map中key记录的索引值相减,最后与k相比较,假如说大了,那就更新这个key,假如小了,那就返回布尔值结果,这样的复杂度能做到log(N)
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])) {
int existIdx = map.get(nums[i]);
if (i - existIdx <= k) {
return true;
}else {
map.put(nums[i], i);
}
} else {
map.put(nums[i], i);
}
}
return false;
}
O(N)解法(2)
第二种解法使用的是Set,和前面Map记录值的不同的是,利用set可以做到类似滑动窗口一样的效果,思路是先将元素加入Set中,但是这个Set的长度不能超过k,假如超过k的话,则需要移除第一个值维护这个窗口的规定大小,然后不断的往前移,知道在i个元素与set里面某个元素相等为止。
public boolean containsNearbyDuplicate(int[] nums, int k) {
Set<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
if (set.contains(nums[i])) {
return true;
}
set.add(nums[i]);
if (set.size() > k) {
set.remove(nums[i - k]);
}
}
return false;
}
回顾217号 存在重复元素
给定一个整数数组,判断是否存在重复元素。
如果任意一值在数组中出现至少两次,函数返回 true 。如果数组中每个元素都不相同,则返回 false 。
如果用O(N)解法一去解,加之前先看看是否存在,这个问题是不是会少很多呢?
在用Set之前,我曾考虑过用最大空间数组,然后对num[i]标记这个思路去解,但是md居然没考虑到这个num[i]有负数存在,失败了,到后面还是用了set,添加之前先检查是否存在了这个值的这个思路,才是正解!
public boolean containsDuplicate(int[] nums) {
Set<Integer> res = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
if (res.contains(nums[i])) {
return true;
}
res.add(nums[i]);
}
return false;
}