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;
    }
};
posted @   Leibnizzzzzz  阅读(124)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示