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)原题链接
欢迎大家和我沟通交流(✿◠‿◠)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值