LeetCode 面试经典 150_哈希表_存在重复元素 II(46_219_C++_简单)
题目描述:
给你一个整数数组 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
题解:
解题思路:
思路一(哈希表):
1、查找相同元素可以想到使用哈希表,key: nums[i] ; value:i。
- 当前元素不存在哈希表中,将元素与对应下标存入哈希表中。
- 当前元素存在哈希表中,判断当前元素的下标 和 之前哈希表中存储元素的下标,如果满足 abs(i - j) <= k 则返回true,若不满足如果满足 abs(i - j) <= k ,则更新哈希表中此元素的对应关系为当前位置。
- 若遍历整个数组没有满足abs(i - j) <= k的值则返回false
2、复杂度分析:
① 时间复杂度:O(n),n 为数组中元素的个数,只遍历了一遍数组。
② 空间复杂度:O(n),最坏的情况下存储整个数组中的元素。
思路二(滑动窗口):
1、left为窗口的左侧,right为窗口的右侧。因right-left <= k 可以自然的想到维护一个 k+1 大小的 滑动窗口, 那么riht - left = k 时(也就是窗口大小为k-1)。left 先右移,right再右移(注意left和right总是右移)
2、复杂度分析
① 时间复杂度:O(n),n 为数组中元素的个数,只遍历了一遍数组。
② 空间复杂度:O(k),最坏只存储 k-1 个元素。
代码实现
代码实现(思路一(哈希表)):
class Solution1 {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
// 使用 unordered_map 来存储数组元素及其对应的索引
unordered_map<int, int> mp;
// 遍历数组
for (int i = 0; i < nums.size(); i++) {
// 检查当前元素是否已存在于 map 中,并且判断当前索引与该元素最后出现的索引之差是否小于等于 k
if (mp.count(nums[i]) && i - mp[nums[i]] <= k) {
// 如果满足条件,说明找到了相同元素且它们的索引之差小于等于 k,返回 true
return true;
}
// 将当前元素及其索引加入 map
mp[nums[i]] = i;
}
// 如果遍历完成没有找到符合条件的元素,返回 false
return false;
}
};
代码实现(思路二(滑动窗口)):
class Solution2 {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
// 使用 unordered_set 来存储当前窗口中的元素
unordered_set<int> set;
// 初始化两个指针,left 和 right,表示滑动窗口的左右边界
int left = 0;
int right = 0;
// 使用 while 循环遍历整个数组,right 指针从左到右扩展
while (right < nums.size()) {
// 如果当前元素在窗口中已经存在,则返回 true,表示找到了重复元素
if (set.count(nums[right])) {
return true;
}
// 将当前元素添加到窗口集合中
set.insert(nums[right]);
// 如果窗口的大小大于等于 k,则移动 left 指针,缩小窗口
while (right - left >= k) {
// 移除窗口最左边的元素
set.erase(nums[left]);
// 左指针向右移动,缩小窗口
++left;
}
// 右指针向右移动,扩大窗口
++right;
}
// 如果遍历完整个数组,仍然没有找到符合条件的重复元素,返回 false
return false;
}
};
/** 滑动窗口,和上方代码是相同的思想
* 当窗口大小固定时我们可以使用if来控制left的增加,for循环来控制right的增加
*/
class Solution3 {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
// 使用 unordered_set 来存储当前窗口中的元素
unordered_set<int> set;
// 初始化左指针 left,表示滑动窗口的左边界
int left = 0;
// 使用 for 循环遍历整个数组,right 指针从左到右扩展
for (int right = 0; right < nums.size(); right++) {
// 如果当前元素 nums[right] 在窗口中已经存在,则返回 true,表示找到了重复元素
if (set.count(nums[right])) {
return true;
}
// 将当前元素 nums[right] 插入到滑动窗口集合中
set.insert(nums[right]);
// 如果当前窗口的大小已经达到或超过 k,移动 left 指针,缩小窗口
if (right - left >= k) {
// 移除窗口最左侧的元素 nums[left],缩小窗口
set.erase(nums[left]);
// 左指针向右移动,缩小窗口
left++;
}
}
// 如果遍历完整个数组,仍然没有找到符合条件的重复元素,返回 false
return false;
}
};
以思路二为例进行调试
#include<iostream>
#include<vector>
#include<unordered_map>
#include<unordered_set>
using namespace std;
class Solution2 {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
// 使用 unordered_set 来存储当前窗口中的元素
unordered_set<int> set;
// 初始化两个指针,left 和 right,表示滑动窗口的左右边界
int left = 0;
int right = 0;
// 使用 while 循环遍历整个数组,right 指针从左到右扩展
while (right < nums.size()) {
// 如果当前元素在窗口中已经存在,则返回 true,表示找到了重复元素
if (set.count(nums[right])) {
return true;
}
// 将当前元素添加到窗口集合中
set.insert(nums[right]);
// 如果窗口的大小大于等于 k,则移动 left 指针,缩小窗口
while (right - left >= k) {
// 移除窗口最左边的元素
set.erase(nums[left]);
// 左指针向右移动,缩小窗口
++left;
}
// 右指针向右移动,扩大窗口
++right;
}
// 如果遍历完整个数组,仍然没有找到符合条件的重复元素,返回 false
return false;
}
};
int main(int argc, char const *argv[])
{
vector<int> nums={1,2,3,1};
int k=3;
Solution2 s;
if (s.containsNearbyDuplicate(nums,3)){
cout<<"true"<<endl;
}else{
cout<<"false"<<endl;
}
return 0;
}
LeetCode 面试经典 150_哈希表_存在重复元素 II(46_219)原题链接
欢迎大家和我沟通交流(✿◠‿◠)