Leetcode-219.[存在重复元素Ⅱ]

41 阅读2分钟

题目描述: 给你一个整数数组 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—— 暴力法(入门理解)

核心逻辑:遍历每个元素,检查它后面最多 k 个元素是否和它重复。

代码示例:

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        int n = nums.size();
        // 边界:数组长度<2 或 k=0(i≠j,索引差不可能≤0),直接返回false
        if (n < 2 || k == 0) return false;
        for (int i = 0; i < n; ++i) {
            // j最多到i+k,且不超过数组末尾
            for (int j = i + 1; j <= min(i + k, n - 1); ++j) {
                if (nums[i] == nums[j]) {
                    return true;
                }
            }
        }
        return false;
    }
};

缺点:

时间复杂度 O (nk),如果 k 接近数组长度 n(比如 n=10^5,k=10^5),会超时,实际面试 / 刷题中不推荐。

思路 2—— 滑动窗口 + 哈希表(最优解)

核心逻辑:

  • 维护一个「大小不超过 k」的滑动窗口(窗口内元素的索引差都≤k);

  • 用 unordered_set 存储窗口内的元素(快速判断当前元素是否在窗口中);

  • 遍历数组时:

    1. 若当前元素在 set 中 → 说明存在重复且索引差≤k,返回 true;
    2. 若不在,将当前元素加入 set;
    3. 若 set 大小超过 k → 移除窗口最左侧的元素(保证窗口大小≤k)。

代码实现(高效且简洁):

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        int n = nums.size();
        if (n < 2 || k == 0) return false;
        
        unordered_set<int> window; // 存储窗口内的元素
        for (int i = 0; i < n; ++i) {
            // 1. 检查当前元素是否在窗口中(满足条件直接返回)
            if (window.count(nums[i])) {
                return true;
            }
            // 2. 加入当前元素到窗口
            window.insert(nums[i]);
            // 3. 窗口大小超过k,移除最左侧的元素(i-k位置的元素)
            if (window.size() > k) {
                window.erase(nums[i - k]);
            }
        }
        // 遍历完无满足条件的元素
        return false;
    }
};

关键解释:

  • 窗口的「左边界」是 i - k,右边界是 i,保证窗口内所有元素的索引差≤k;
  • unordered_set 的 count/insert/erase 操作平均时间复杂度都是 O (1),整体时间复杂度 O (n),空间复杂度 O (k)(最多存储 k 个元素);
  • 边界处理:提前判断 n<2(无两个元素)、k=0(索引差必须 > 0),避免无效遍历。

对比两种解法:

解法时间复杂度空间复杂度适用场景
暴力法O(nk)O(1)k 很小(比如 k≤100)
滑动窗口 + 哈希O(n)O(k)任意 k(推荐)