1 题目
设计一个算法,找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。
示例 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]});
这一行代码不知道这么写,但是见过了就知道了,见过了就会了。
解决思路回顾
- 排序数组:先对数组进行升序排序,为双指针遍历做准备。
- 双指针遍历 :左指针从数组头部开始,右指针从数组尾部开始,根据两数之和与目标值的大小关系移动指针:
- 和等于目标值:记录数对,同时移动左右指针(保证一个数只属于一个数对)。
- 和小于目标值:左指针右移(增大和)。
- 和大于目标值:右指针左移(减小和)。
- 返回结果:将所有符合条件的数对存入二维向量返回。
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;
}
*/
代码关键部分解释
- 排序 :使用 C++ 标准库的
sort函数,时间复杂度为\(O(n \log n)\),对于 10 万级别的数据效率很高,且该函数是原地排序,空间开销小。 - 双指针逻辑 :
- 循环条件
left < right确保不会出现单个数字自成数对的情况,也保证了每个数字只被访问一次。 - 找到数对后必须同时移动
left和right,这是满足一个数只能属于一个数对的核心操作。
- 循环条件
- 结果存储 :使用
vector<vector<int>>存储结果,这是 LeetCode 中处理二维整数结果的标准方式,push_back方法可以高效添加数对。
注意事项
- 无需提交
main函数和测试代码,只需要提交Solution类及其成员函数即可。 - 代码中已经包含了必要的头文件(
vector和algorithm),这是 LeetCode 编译通过的前提。 - 该代码的时间复杂度为\(O(n \log n)\)(排序主导),空间复杂度为\(O(1)\)(忽略排序的系统栈空间和结果存储的空间),能够高效处理题目中
nums.length <= 100000的限制。
总结
- C++ 实现的核心依然是排序 + 双指针,这是处理该问题的最优思路之一,兼顾效率和代码简洁性。
- 关键要点是排序后双指针的移动逻辑,尤其是找到数对后同时移动左右指针,避免数字重复使用。
- 代码符合 LeetCode 的提交规范,直接复制即可提交,且能通过所有测试。
3 题目
给你一个整数数组 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] <= 1090 <= 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 的功能,但你写的代码里有核心逻辑错误、循环语法错误,我先帮你逐一分析问题,再给出正确的实现方式。
先分析你代码中的问题
- 排序操作破坏了原始索引(致命错误) :
sort(nums.begin(), nums.end())会把数组元素重新排序,这会完全打乱元素的原始索引。而题目要求的是原始索引 的差值不超过k,排序后索引信息丢失,后续判断毫无意义。 - 内层循环的三重错误 :
- 初始值:
j = 1是固定值,应该从i + 1开始(或用更高效的方式); - 循环条件:
j > i是错误的(比如i=0时,j=1满足,但i=1时,j=1就不满足了,且后续永远不满足); - 自增变量:循环体里写的是
i++而非j++,会导致i无限递增,最终数组越界。
- 初始值:
- 时间复杂度问题:即使修复循环,双重循环的时间复杂度是 (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;
}
};
代码关键部分解释
- 哈希集合
window:unordered_set的count方法可以在 \(O(1)\) 时间内判断元素是否存在,insert和erase操作也都是 \(O(1)\) 时间,保证了效率。 - 滑动窗口的维护 :
- 遍历到索引
i时,窗口包含的是索引范围在i-k到i的元素(如果i <= k,则是0到i)。 - 当窗口大小超过
k时,移除索引为i-k的元素,确保窗口内的元素索引差永远不超过k。
- 遍历到索引
- 提前返回 :一旦发现当前元素在窗口中,直接返回
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)。
总结
- 核心错误修正:去掉了打乱索引的排序操作,修复了内层循环的语法和逻辑错误,改用哈希集合实现 \(O(n)\) 时间复杂度的解法。
- 滑动窗口思想 :用哈希集合维护长度为
k的窗口,是解决 "索引差限制的重复元素" 问题的最优思路。 - 性能优化:利用哈希表的快速查找特性,避免了双重循环的低效问题,同时提前返回减少了不必要的遍历。