题目描述
两数之和意思就是
给你一个数组,从中找出两个数字,让他们的和等于一个具体的target。找到所有这样的两个数。并且这两个数字不能完全一样。
n数之和的意思是
给你一个数组,从中找出n个数字,让他们的和等于一个具体的target。找到所有这样的n个数。并且这n个数字不能完全一样。
最基础的,也是最关键的就是两数之和。
解法
排序 + 双指针
排序之后,一个指针指向头,一个指针指向尾。
如果这两个数的和大于了target,说明数组中没有了和尾之和等于target的数字了,所以这个数字就可以排除了,于是尾指针减一。
否则就是尾指针和头指针的和小于等于target。这时候
如果等于就放入答案,如果小于就让头指针加上1。
那么怎么来去重呢?
保证每个相同的数字只被使用一次。由于排序之后,相同的数字相邻,所以只需要相邻的数字相同的时候不在进入循环就可以实现去重操作。
伪代码
while(){
while(尾指针内容和头指针内容之和大于 target){
尾指针前移
}
if(内容之和等于target) 放入答案。
//内容和小于targe
头移动;
while(相邻的内容相同) 头指针后移;
}
代码
vector<vector<int>> twoSum(vector<int>& nums, int target, int start){
vector<vector<int>> ans;
int n = nums.size(), j = start, k = n - 1;
while(j < k){
while(j < k && nums[j] + nums[k] > target) k--;
int a = nums[j], b = nums[k];
if(j < k && b + a == target) ans.push_back({a, b});
while(j < k && nums[j] == a) j++;
}
return ans;
}
三数之和和四数之和
由于三数之和和四数之和只不过是在双数之和的基础上在加一层遍历,所以不在赘述。
- 三数之和
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
sort(nums.begin(), nums.end());
int n = nums.size(), i = 0;
while(i < n) {
int j = i + 1, k = n - 1, a = nums[i];
while(j < k){
while(j < k && nums[j] + nums[k] < -a) {j++;}
while(j < k && nums[j] + nums[k] > -a) {k--;}
int b = nums[j], c = nums[k];
if(j < k && a + b + c == 0) ans.push_back({a, b, c});
while(j < k && nums[j] == b) j++;
}
while(i < n && nums[i] == a) i++;
}
return ans;
}
- 四数之和
vector<vector<int>> threeSum(vector<int>& nums, int target, int start) {
vector<vector<int>> ans;
int n = nums.size(), i = start;
while(i < n) {
int j = i + 1, k = n - 1, a = target - nums[i];
while(j < k){
while(j < k && nums[j] + nums[k] > a) {k--;}
int b = nums[j], c = nums[k];
if(j < k && b + c == a) ans.push_back({target - a, b, c});
while(j < k && nums[j] == b) j++;
}
while(i < n && nums[i] == target - a) i++;
}
return ans;
}
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
for(int i = 0; i < n; i++){
if(i > 0 && nums[i] == nums[i - 1]) continue;
int a = nums[i];
vector<vector<int>> tmp = threeSum(nums, target - a, i + 1);
for(auto vec: tmp){
vec.push_back(a);
ans.push_back(vec);
}
}
return ans;
}
总结代码
这里使用三数之和调用双数之和。
四数之和调用三数之和。
结构化之后,可以更方便使用。
这里的四数之和也可以调用双数之和,这样是更优化的,但是比较懒…
所以就出现了内容的部分耦合。
结果就是,双数之和是最基本也是最关键的。其他的N数都是在此基础之上加上n-2重循环。
vector<vector<int>> twoSum(vector<int>& nums, int target, int start){
//printf("two\n");
vector<vector<int>> ans;
int n = nums.size(), j = start, k = n - 1;
while(j < k){
while(j < k && nums[j] + nums[k] > target) k--;
int a = nums[j], b = nums[k];
if(j < k && b + a == target) ans.push_back({a, b});
while(j < k && nums[j] == a) j++;
}
return ans;
}
vector<vector<int>> threeSum(vector<int>& nums, int target, int start) {
//printf("three\n");
vector<vector<int>> ans;
int n = nums.size(), i = start;
while(i < n) {
int a = target - nums[i];
vector<vector<int>> tmp = twoSum(nums, a, i + 1);
for(auto vec: tmp){
vec.push_back(target - a);
ans.push_back(vec);
}
while(i < n && nums[i] == target - a) i++;
}
return ans;
}
vector<vector<int>> fourSum(vector<int>& nums, int target) {
//printf("foru\n");
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
for(int i = 0; i < n; i++){
if(i > 0 && nums[i] == nums[i - 1]) continue;
int a = target - nums[i];
vector<vector<int>> tmp = threeSum(nums, a, i + 1);
for(auto vec: tmp){
vec.push_back(target - a);
ans.push_back(vec);
}
}
return ans;
}