1、2Sum
如果假设输入一个数组 nums 和一个目标和 target,请你返回 nums 中能够凑出 target 的两个元素的值,比如输入 nums = [5,3,1,6], target = 9,那么算法返回两个元素 [3,6]。nums 中可能有多对儿元素之和都等于 target,请你的算法返回所有和为 target 的元素对儿,其中不能出现重复。
解析:
可以先对 nums 排序,然后利用前文「双指针技巧汇总」写过的左右双指针技巧,从两端相向而行就行了。但关键难点是现在可能有多个和为 target 的数对儿,还不能重复,
出问题的地方在于 sum == target 条件的 if 分支,当给 res 加入一次结果后,lo 和 hi 不应该改变 1 的同时,还应该跳过所有重复的元素,这样就可以保证一个答案只被添加一次,重复的结果都会被跳过,可以得到正确的答案。不过,受这个思路的启发,其实前两个 if 分支也是可以做一点效率优化,跳过相同的元素。
vector<vector<int>> twoSumTarget(vector<int>& nums, int target) {
// nums 数组必须有序
sort(nums.begin(), nums.end());
int lo = 0, hi = nums.size() - 1;
vector<vector<int>> res;
while (lo < hi) {
int sum = nums[lo] + nums[hi];
int left = nums[lo], right = nums[hi];
// 根据 sum 和 target 的比较,移动左右指针
if (sum < target) {
while (lo < hi && nums[lo] == left) lo++;
} else if (sum > target) {
while (lo < hi && nums[hi] == right) hi--;
} else {
res.push_back({left, right});//出问题的地方在于 sum == target 条件的 if 分支,当给 res 加入一次结果后,lo 和 hi 不应该改变 1 的同时,还应该跳过所有重复的元素:
while (lo < hi && nums[lo] == left) lo++;
while (lo < hi && nums[hi] == right) hi--;
}
}
return res;
}
这个函数的时间复杂度非常容易看出来,双指针操作的部分虽然有那么多 while 循环,但是时间复杂度还是 O(N),而排序的时间复杂度是 O(NlogN),所以这个函数的时间复杂度是 O(NlogN)。
2、3Sum
力扣15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c
= 0 ?请你找出所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。示例 1: 输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 示例 2: 输入:nums
= [] 输出:[] 示例 3: 输入:nums = [0] 输出:[]
解析:
本题是三数之和,借助于二数之和的模板,通过遍历第一个数i,然后调用二数之和函数,从i+1开始,二数之和的target=三数之和的target(0)-nums[i],
代码:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
int n=nums.size();
vector<vector<int>> res;
for(int i=0;i<n;i++){
vector<vector<int>> tuples=twoSum(nums,i+1,0-nums[i]);
for(vector<int>& tuple:tuples){
tuple.push_back(nums[i]);
res.push_back(tuple);
}
while(i<n-1 && nums[i]==nums[i+1]) i++;// 跳过第一个数字重复的情况,否则会出现重复结果
}
return res;
}
vector<vector<int>> twoSum(vector<int>& nums,int start,int target){
int n=nums.size();
int lo=start,hi=n-1;
vector<vector<int>> res;
while(lo<hi){
int sum=nums[lo]+nums[hi];
int left=nums[lo],right=nums[hi];
if(sum<target){
while(lo<hi && nums[lo]==left) lo++;
}else if(sum>target){
while(lo<hi && nums[hi]==right) hi--;
}else{
res.push_back({left,right});
while(lo<hi && nums[lo]==left) lo++;
while(lo<hi && nums[hi]==right) hi--;
}
}
return res;
}
};
4、4Sum问题
力扣18四数之和
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组
[nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):0 <= a, b, c, d < n a、b、c 和 d 互不相同 nums[a] + nums[b] + nums[c] +
nums[d] == target 你可以按 任意顺序 返回答案 。示例 1: 输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]] 示例 2:输入:nums = [2,2,2,2,2], target = 8 输出:[[2,2,2,2]]
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/4sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
nSum 本题借助三数之和思想,遍历第一个数,然后调用三数之和的函数,得到后三个元组,然后这四个数相加之和为target
代码
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
int n=nums.size();
vector<vector<int>> res;
for(int i=0;i<n;i++){
vector<vector<int>> triples=threeSum(nums,i+1,target-nums[i]);
for(vector<int>& triple:triples){
triple.push_back(nums[i]);
res.push_back(triple);
}
while(i<n-1 && nums[i]==nums[i+1]) i++;
}
return res;
}
vector<vector<int>> threeSum(vector<int>& nums,int start,int target){
int n=nums.size();
vector<vector<int>> res;
for(int i=start;i<n;i++){
vector<vector<int>> tuiples=twoSum(nums,i+1,target-nums[i]);
for(vector<int>& tuiple:tuiples){
tuiple.push_back(nums[i]);
res.push_back(tuiple);
}
while(i<n-1 && nums[i]==nums[i+1]) i++;
}
return res;
}
vector<vector<int>> twoSum(vector<int>& nums,int start,int target){
int n=nums.size();
int lo=start,hi=n-1;
vector<vector<int>> res;
while(lo<hi){
int sum=nums[lo]+nums[hi];
int left=nums[lo],right=nums[hi];
if(sum<target){
while(lo<hi && nums[lo]==left) lo++;
}else if(sum>target){
while(lo<hi && nums[hi]==right) hi--;
}else{
res.push_back({left,right});
while(lo<hi && nums[lo]==left) lo++;
while(lo<hi && nums[hi]==right) hi--;
}
}
return res;
}
};
5、nSum问题
总结上面的
/* 注意:调用这个函数之前一定要先给 nums 排序 */
vector<vector<int>> nSumTarget(
vector<int>& nums, int n, int start, int target) {
int sz = nums.size();
vector<vector<int>> res;
// 至少是 2Sum,且数组大小不应该小于 n
if (n < 2 || sz < n) return res;
// 2Sum 是 base case
if (n == 2) {
// 双指针那一套操作
int lo = start, hi = sz - 1;
while (lo < hi) {
int sum = nums[lo] + nums[hi];
int left = nums[lo], right = nums[hi];
if (sum < target) {
while (lo < hi && nums[lo] == left) lo++;
} else if (sum > target) {
while (lo < hi && nums[hi] == right) hi--;
} else {
res.push_back({left, right});
while (lo < hi && nums[lo] == left) lo++;
while (lo < hi && nums[hi] == right) hi--;
}
}
} else {
// n > 2 时,递归计算 (n-1)Sum 的结果
for (int i = start; i < sz; i++) {
vector<vector<int>>
sub = nSumTarget(nums, n - 1, i + 1, target - nums[i]);
for (vector<int>& arr : sub) {
// (n-1)Sum 加上 nums[i] 就是 nSum
arr.push_back(nums[i]);
res.push_back(arr);
}
while (i < sz - 1 && nums[i] == nums[i + 1]) i++;
}
}
return res;
}
例如上面4Sum问题套用模板
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
int n=nums.size();
vector<vector<int>> res;
for(int i=0;i<n;i++){
vector<vector<int>> triples=nsum(nums,3,i+1,target-nums[i]);
for(vector<int>& triple:triples){
triple.push_back(nums[i]);
res.push_back(triple);
}
while(i<n-1 && nums[i]==nums[i+1]) i++;
}
return res;
}
vector<vector<int>> nsum(vector<int>& nums,int n,int start,int target){
int sz=nums.size();
vector<vector<int>> res;
if(n<2 || sz<n) return res;
if(n==2){
int lo=start,hi=sz-1;
while(lo<hi){
int sum=nums[lo]+nums[hi];
int left=nums[lo], right=nums[hi];
if(sum<target){
while(lo<hi && nums[lo]==left) lo++;
}else if(sum>target){
while(lo<hi && nums[hi]==right) hi--;
}else{
res.push_back({left,right});
while(lo<hi && nums[lo]==left) lo++;
while(lo<hi && nums[hi]==right) hi--;
}
}
}
else{
for(int i=start;i<sz;i++){
vector<vector<int>> sub=nsum(nums,n-1,i+1,target-nums[i]);
for(vector<int>& arr:sub){
arr.push_back(nums[i]);
res.push_back(arr);
}
while(i<sz-1 && nums[i]==nums[i-1]) i++;
}
}
return res;
}
};