Leetcode 75 数对和 | 存在重复元素 II

1 题目

面试题 16.24. 数对和

设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。

示例 1:

复制代码
输入:nums = [5,6,5], target = 11
输出:[[5,6]]

示例 2:

复制代码
输入:nums = [5,6,5,6], target = 11
输出:[[5,6],[5,6]]

提示:

  • nums.length <= 100000
  • -105 <= nums[i], target <= 105

2 代码实现

sort排序一下,双指针,唯一有点困惑就是这个输出打印我不是太会写。

cpp 复制代码
class Solution {
public:
    vector<vector<int>> pairSums(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> result ;

        int left = 0 ;
        int right = nums.size() - 1 ;

        while (left < right) {
            int current_sum = nums[left] + nums[right] ;
            if(current_sum == target){
                result.push_back({nums[left] , nums[right]});
                left ++;
                right --;
            }else if (current_sum < target){
                left++;
            }else {
                right--;
            }
        }
        return result ;
    }
};

和前面一则Leetcode 74 K 和数对的最大数目-CSDN博客没有区别,没有难度,就这个:

cpp 复制代码
  result.push_back({nums[left] , nums[right]});

这一行代码不知道这么写,但是见过了就知道了,见过了就会了。

解决思路回顾

  1. 排序数组:先对数组进行升序排序,为双指针遍历做准备。
  2. 双指针遍历 :左指针从数组头部开始,右指针从数组尾部开始,根据两数之和与目标值的大小关系移动指针:
    • 和等于目标值:记录数对,同时移动左右指针(保证一个数只属于一个数对)。
    • 和小于目标值:左指针右移(增大和)。
    • 和大于目标值:右指针左移(减小和)。
  3. 返回结果:将所有符合条件的数对存入二维向量返回。

C++ 核心代码实现

cpp 复制代码
#include <vector>
#include <algorithm> // 包含sort函数

using namespace std;

class Solution {
public:
    vector<vector<int>> pairSums(vector<int>& nums, int target) {
        // 步骤1:对数组进行升序排序
        sort(nums.begin(), nums.end());
        // 存储结果的二维向量
        vector<vector<int>> result;
        // 初始化双指针
        int left = 0;
        int right = nums.size() - 1;
        
        // 双指针遍历,left < right 保证不重复取数且一个数只属于一个数对
        while (left < right) {
            int current_sum = nums[left] + nums[right];
            if (current_sum == target) {
                // 找到数对,添加到结果中
                result.push_back({nums[left], nums[right]});
                // 同时移动指针,避免重复使用数字
                left++;
                right--;
            } else if (current_sum < target) {
                // 和太小,左指针右移增大和
                left++;
            } else {
                // 和太大,右指针左移减小和
                right--;
            }
        }
        
        return result;
    }
};

// 以下为本地测试代码(LeetCode中无需提交这部分)
/*
#include <iostream>
int main() {
    Solution sol;
    // 示例1
    vector<int> nums1 = {5, 6, 5};
    int target1 = 11;
    vector<vector<int>> res1 = sol.pairSums(nums1, target1);
    for (auto& pair : res1) {
        cout << "[" << pair[0] << "," << pair[1] << "]";
    }
    cout << endl; // 输出:[5,6]
    
    // 示例2
    vector<int> nums2 = {5, 6, 5, 6};
    int target2 = 11;
    vector<vector<int>> res2 = sol.pairSums(nums2, target2);
    for (auto& pair : res2) {
        cout << "[" << pair[0] << "," << pair[1] << "]";
    }
    cout << endl; // 输出:[5,6][5,6]
    
    return 0;
}
*/

代码关键部分解释

  1. 排序 :使用 C++ 标准库的sort函数,时间复杂度为\(O(n \log n)\),对于 10 万级别的数据效率很高,且该函数是原地排序,空间开销小。
  2. 双指针逻辑
    • 循环条件left < right确保不会出现单个数字自成数对的情况,也保证了每个数字只被访问一次。
    • 找到数对后必须同时移动leftright,这是满足一个数只能属于一个数对的核心操作。
  3. 结果存储 :使用vector<vector<int>>存储结果,这是 LeetCode 中处理二维整数结果的标准方式,push_back方法可以高效添加数对。

注意事项

  • 无需提交main函数和测试代码,只需要提交Solution类及其成员函数即可。
  • 代码中已经包含了必要的头文件(vectoralgorithm),这是 LeetCode 编译通过的前提。
  • 该代码的时间复杂度为\(O(n \log n)\)(排序主导),空间复杂度为\(O(1)\)(忽略排序的系统栈空间和结果存储的空间),能够高效处理题目中nums.length <= 100000的限制。

总结

  1. C++ 实现的核心依然是排序 + 双指针,这是处理该问题的最优思路之一,兼顾效率和代码简洁性。
  2. 关键要点是排序后双指针的移动逻辑,尤其是找到数对后同时移动左右指针,避免数字重复使用。
  3. 代码符合 LeetCode 的提交规范,直接复制即可提交,且能通过所有测试。

3 题目

219. 存在重复元素 II

给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个 不同的索引 ij ,满足 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

4 代码实现

cpp 复制代码
class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_set<int> window ;
        int n = nums.size();
        for(int i = 0 ; i < n ; i++){
            if(window.count(nums[i])){
                return true;
            }
            window.insert(nums[i]);

            if(window.size() > k ){
                window.erase(nums[i - k ]);
            }
        }
        return false;
    }
};

元素定距离我应该想到是滑动窗口的哎呀...

遇到数组中元素的索引 / 位置有固定距离限制 (如≤k、≥k、=k)的问题,优先考虑滑动窗口(配合哈希表 / 集合优化查找)。

错误百出的代码

cpp 复制代码
class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        sort(nums.begin(),nums.end());
        for (int i = 0 ; i < nums.size() ; i ++){
            for (int j = 1 ; j > i ; i++){
                if (nums[i] == nums[j] && abs(i - j) <= k){
                    return true;
                }
            }
        }
        return false;
    }
};

你现在想要实现的是判断数组中是否存在两个相同元素,且它们的索引差的绝对值不超过 k 的功能,但你写的代码里有核心逻辑错误、循环语法错误,我先帮你逐一分析问题,再给出正确的实现方式。

先分析你代码中的问题

  1. 排序操作破坏了原始索引(致命错误)sort(nums.begin(), nums.end()) 会把数组元素重新排序,这会完全打乱元素的原始索引。而题目要求的是原始索引 的差值不超过 k,排序后索引信息丢失,后续判断毫无意义。
  2. 内层循环的三重错误
    • 初始值:j = 1 是固定值,应该从 i + 1 开始(或用更高效的方式);
    • 循环条件:j > i 是错误的(比如 i=0 时,j=1 满足,但 i=1 时,j=1 就不满足了,且后续永远不满足);
    • 自增变量:循环体里写的是 i++ 而非 j++,会导致 i 无限递增,最终数组越界。
  3. 时间复杂度问题:即使修复循环,双重循环的时间复杂度是 (O(n^2)),当数组元素很多时,效率会非常低。

正确的实现方案(滑动窗口 + 哈希集合)

这是解决该问题的最优方法 ,时间复杂度为(O(n)),空间复杂度为 (O(k)),思路是用哈希集合维护一个长度为 k 的滑动窗口,确保窗口内的元素索引差不超过 k

cpp 复制代码
#include <vector>
#include <unordered_set>
using namespace std;

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        // 哈希集合:存储当前滑动窗口内的元素
        unordered_set<int> window;
        int n = nums.size();
        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;
    }
};

代码关键部分解释

  1. 哈希集合 windowunordered_setcount 方法可以在 \(O(1)\) 时间内判断元素是否存在,inserterase 操作也都是 \(O(1)\) 时间,保证了效率。
  2. 滑动窗口的维护
    • 遍历到索引 i 时,窗口包含的是索引范围在 i-ki 的元素(如果 i <= k,则是 0i)。
    • 当窗口大小超过 k 时,移除索引为 i-k 的元素,确保窗口内的元素索引差永远不超过 k
  3. 提前返回 :一旦发现当前元素在窗口中,直接返回 true,无需继续遍历,优化了性能。

测试用例(帮助理解)

  • 输入:nums = [1,2,3,1], k = 3 → 输出:true(索引 0 和 3 的元素都是 1,差为 3≤3)。
  • 输入:nums = [1,0,1,1], k = 1 → 输出:true(索引 2 和 3 的元素都是 1,差为 1≤1)。
  • 输入:nums = [1,2,3,1,2,3], k = 2 → 输出:false(所有重复元素的索引差都大于 2)。

总结

  1. 核心错误修正:去掉了打乱索引的排序操作,修复了内层循环的语法和逻辑错误,改用哈希集合实现 \(O(n)\) 时间复杂度的解法。
  2. 滑动窗口思想 :用哈希集合维护长度为 k 的窗口,是解决 "索引差限制的重复元素" 问题的最优思路。
  3. 性能优化:利用哈希表的快速查找特性,避免了双重循环的低效问题,同时提前返回减少了不必要的遍历。
相关推荐
ZXF_H2 小时前
C/C++ OpenSSL自适应格式解析证书二进制字节流
c语言·开发语言·c++·openssl
九河云2 小时前
直播电商数字化:用户行为 AI 分析与选品推荐算法平台建设
人工智能·物联网·算法·推荐算法
CoovallyAIHub2 小时前
深大团队UNeMo框架:让机器人学会“预判”,效率提升40%
深度学习·算法·计算机视觉
神仙别闹2 小时前
基于QT(C++)实现B树可视化
c++·b树·qt
咕咕嘎嘎10242 小时前
C++模板特化
c++
副露のmagic2 小时前
更弱智的算法学习 day9
python·学习·算法
ULTRA??2 小时前
RUST是移动语义与copy trait
算法·rust
小O的算法实验室2 小时前
2022年AEI SCI1区TOP,用蚁群算法求解无人机配送车辆路径规划问题,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
BestOrNothing_20152 小时前
C++ 函数类型大全:成员函数 / 非成员函数 / 全局函数 / 静态函数 / 特殊成员函数 / 虚函数 / 模板函数 全面总结
c++·面向对象·八股·函数·程序语言