1)twoSum
返回nums中所有两个元素之和等于target的对,并且不能出现重复的。
vector<vector<int>> twoSumTarget(vector<int>& nums, int target){
sort(nums.begin(), nums.end());
vector<vector<int>> res;
int l = 0;
int r = nums.size() - 1;
while(l < r){
int sum = nums[l] + nums[r];
int left = nums[l];
int right = nums[r];
if(sum == target){
res.push_back({left, right});
//下面两行代码可以保证不出现重复的元素
while(l < r && nums[l] == left) l++;
while(l < r && nums[r] == right) r--;
}else if(sum > target){
//直接r--也可以,下面这行代码是对r--进行了优化
while(l < r && nums[r] == right) r--;
}else if(sum < target){
while(l < r && nums[l] == left) l++;
}
}
return res;
}
时间复杂度是O(N*logN)
2)threeSum(LeetCode 15)
这个问题会调用上面的求两数之和的函数。对问题线排序,然后这个问题用穷举的思路,确定一个nums[i],剩余的两数之和就是target - nums[i]。这就可以调用两数之和的函数来解决。最后后的一个for循环是排除第一个数字重复的情况,后面两个数字不重复的情况由两数之和的函数确定。时间复杂度是O(N * N)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
return threeSumTarget(nums, 0);
}
//返回3数之和为target的所有组合并且组合不能重复
vector<vector<int>> threeSumTarget(vector<int>& nums, int target) {
vector<vector<int>> res;
sort(nums.begin(), nums.end());
int size = nums.size();
for(int i = 0; i < size; i++){
vector<vector<int>> tuples = twoSumTarget(nums, i + 1, target - nums[i]);
//如果存在满足条件的二元组,再加上nums[i]即为一个结果
int n = tuples.size();
for(int j = 0; j < n; j++){
tuples[j].push_back(nums[i]);
res.push_back(tuples[j]);
}
//跳过第一个数字重复的情况,否则会出现重复的结果
while(i < size - 1 && nums[i] == nums[i + 1]){
i++;
}
}
return res;
}
vector<vector<int>> twoSumTarget(vector<int>& nums, int start, int target){
sort(nums.begin(), nums.end());
vector<vector<int>> res;
int l = start;
int r = nums.size() - 1;
while(l < r){
int sum = nums[l] + nums[r];
int left = nums[l];
int right = nums[r];
if(sum == target){
res.push_back({left, right});
//下面两行代码可以保证不出现重复的元素
while(l < r && nums[l] == left) l++;
while(l < r && nums[r] == right) r--;
}else if(sum > target){
//直接r--也可以,下面这行代码是对r--进行了优化
while(l < r && nums[r] == right) r--;
}else if(sum < target){
while(l < r && nums[l] == left) l++;
}
}
return res;
}
};
4)fourSum(LeetCode 18)
4数之和使用相同的思路,穷举第一个数字,然后调用3数之和的函数,最后排除一下第一个数字重复的情况。时间复杂度为O(N * N * N)
5)nSum
根据上面的规律总结一个这类题的万能函数,需要注意的是在调用该函数前应该先对数组排序。值得复习
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
if(n == 2){
int l = start;
int r = nums.size() - 1;
while(l < r){
int sum = nums[l] + nums[r];
int left = nums[l];
int right = nums[r];
if(sum == target){
res.push_back({left, right});
//下面两行代码可以保证不出现重复的元素
while(l < r && nums[l] == left) l++;
while(l < r && nums[r] == right) r--;
}else if(sum > target){
//直接r--也可以,下面这行代码是对r--进行了优化
while(l < r && nums[r] == right) r--;
}else if(sum < target){
while(l < r && nums[l] == left) l++;
}
}
}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]);
int num = sub.size();
for(int j = 0; j < num; j++){
sub[j].push_back(nums[i]);
res.push_back(sub[j]);
}
while(i < sz - 1 && nums[i] == nums[i + 1]) i++;
}
}
return res;
}