N数之和(力扣 二三四数之和总结)
学习笔记六 N数之和
题目分析
本类题可分为两类,一种是利用哈希表直接进行查找,另一种是利用双指针进行双向查找。
- 哈希表
力扣01.两数之和
力扣454.四数相加
需要利用哈希表时的题目特征:
1,多个数不来自于同一个数组,这样不需要考虑同一数组中重复选择数字的问题。
2,如果数字是来自同一个数组,那么不超过两个。
由于利用哈希表时是借助其键值对 key 的值进行查找,因此通常使用 target - nums[ i ] 的方式进行,这就限定了操作数只能有两个。
- 双指针
力扣15.三数之和
力扣18.四数之和
双指针可以将暴力搜索的 O(n2 ) 复杂度降到 O(n) 。
那么使用双指针的入手思路和暴力解法应该是一样的,只是能减少暴力解法的一层时间复杂度。
注意:如果时间复杂度不超,应该先对数组进行排序 O(nlog n)。
哈希表类型
以四数相加为例:力扣454.四数相加
四数相加
题目介绍:给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。
思路:此时 target 的值为 0 是提前确定好的,而 A , B , C , D 均是不同的数组,这显然用哈希法。上面也说了,哈希法的操作数只能有两个,因此我们需要提前给 A , B , C , D 分组。
- 将 A,B 作为一组存入哈希表,再将 C,D 分为一组,在哈希表里面查询 target - (c + d)是否存在即可。
class Solution {
public:
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
// 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中
for (int a : A) {
for (int b : B) {
umap[a + b]++;
}
}
int count = 0; // 统计a+b+c+d = 0 出现的次数
// 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
for (int c : C) {
for (int d : D) {
if (umap.find(0 - (c + d)) != umap.end()) {
count += umap[0 - (c + d)];
}
}
}
return count;
}
};
双指针类型
以四数之和为例:力扣18.四数之和
四数之和
题目介绍:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
思路:由于a, b, c, d 均在同一数组中,我们只能考虑双指针简化暴力解法。但是要注意,每层循环都要有判断是否重复和减少比较次数的考虑。
1,准备工作
vector<vector<int>> quadruplets; // 答案数组
if (nums.size() < 4) {
return quadruplets;
}
sort(nums.begin(), nums.end()); // 双指针必须排序
int length = nums.size();
2,第一重循环
for (int i = 0; i < length - 3; i++) {
// 循环体内内容见下面
}
- 由于四元组不能重复,因此要判断当前的 nums[i] 是否和上一个 nums[i - 1] 相等。
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
- 如果与 nums[i] 相邻的三个数之和已经大于 target,那么就没有继续比对下去的必要,直接break;
- 如果 nums[i] 和最后三个数字(最大的三个数字)之和小于 target,那就说明这个 i 太小,直接 continue 进入 i+1 的循环。
if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break; // 用long防止溢出
}
if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
3,第二重循环
第二重循环是嵌套在第一重循环里面,而且循环体的内容也基本一致。
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
break; // 这里 i 已经确定了,只用比较 j 后两位即可
}
if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
4,双指针部分
双指针部分是嵌套在第二重循环里面。
- 先定义左指针和右指针
- 左指针是较小数,左指针右移,四数之和增加
- 右指针是较大数,右指针左移,四数之和减小
int left = j + 1, right = length - 1;
while (left < right) {
//循环体内内容见下面
}
- 用 sum 储存四数之和并与 target 比较,如果小于target ,左指针右移;如果大于 target ,右指针左移。
- 如果等于 target,保存这一组四元组 [i, j, left, right] ,并且检查下一个 left 和 right 是否和当前的相等,如果相等,递增直到不相等。(去重,防止获得有重复的四元组)
while (left < right) {
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
quadruplets.push_back({nums[i], nums[j], nums[left], nums[right]});
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
left++;
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
right--;
}
else if (sum < target) {
left++;
}
else {
right--;
}
}
5,完整代码
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> quadruplets;
if (nums.size() < 4) {
return quadruplets;
}
sort(nums.begin(), nums.end());
int length = nums.size();
for (int i = 0; i < length - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
if ((long) nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) {
break;
}
if ((long) nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
for (int j = i + 1; j < length - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
if ((long) nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) {
break;
}
if ((long) nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target) {
continue;
}
int left = j + 1, right = length - 1;
while (left < right) {
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
quadruplets.push_back({nums[i], nums[j], nums[left], nums[right]});
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
left++;
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
}
return quadruplets;
}
};
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库