「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」
前言
利用 滑动窗口加上set表优化算法
题目描述
今天的题目为leetcode上的 219. 存在重复元素 II 难度为 简单
- 给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 i 和 j ,满足 nums[i] == nums[j] 且 abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false 。
示例 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
复制代码
提示:
- 1 <= nums.length <= 105
- -109 <= nums[i] <= 109
- 0 <= k <= 105
题解
滑动窗口
首先来介绍一下滑动窗口算法的思维,滑动窗口算法是在给定特定窗口大小的数组或字符串上执行要求的操作。 用简单点的说法就是,我们可以将一个很长的数据,通过我们的要求,来将它分割成多分小数据,然后在每个满足条件的小数据中去做一个剩余操作。
举一个例子,上述题目中,如果 k 为 2 ,那么就是说,我们可以定义一个窗口大小为 3 ,在这个窗口中去查找我们需要的相等元素,因为如果两个元素相等,但是它们不存在一个窗口内部,这样就说明它们的下标相减一定是大于 k 的,那就不需要考虑了。然后我们像下图一样一个元素一个元素向前滑动我们的窗口,在每次滑动中去判断窗口中是否有相同的元素,有就返回true,没有则进行下一次滑动,当整个数组滑动完成后,还没有返回true,那就返回false。
map表解法
我们可以通过一个map表来保存之前出现过的元素和它的下标,当有map表里面的相同元素出现后,去判断最近的相同元素保存的下标和当前的差值满不满足要求,这样遍历一遍数组来进行判断。
/**
* @param {number[]} nums
* @param {number} k
* @return {boolean}
*/
var containsNearbyDuplicate = function(nums, k) {
const map = new Map();
for (let i = 0; i < nums.length; i++) {
if (map.has(nums[i]) && i - map.get(nums[i]) <= k) {
return true;
}
map.set(nums[i], i);
}
return false;
};
复制代码
利用滑动窗口和set表进行优化
通过我们上面说到的滑动窗口的原理,加上我们可以用一个set表来保存每一个窗口的元素,当窗口中出现重复的元素的时候,set 表会自动进行去重,所以我们就可以省去判断每个窗口是否含有重复元素的这一步,每次去判断 set 表的长度是否变短就能够知道滑动窗口中是否出现重复元素。然后每次滑动的时候将新的元素保存进来,最后的那个元素弹出 set 表。
如同下图,滑动过后 d 存入 set 表, a 弹出 set 表,然后判断set长度是否减少,减少返回true,以此类推,遍历完成后返回false
/**
* @param {number[]} nums
* @param {number} k
* @return {boolean}
*/
var containsNearbyDuplicate = function(nums, k) {
const set = new Set();
for (let i = 0; i < nums.length; i++) {
if (i > k) {
set.delete(nums[i - k - 1]);
}
if (set.has(nums[i])) {
return true;
}
set.add(nums[i])
}
return false;
};
复制代码