在一维数组总求各种sum的题,大都是通过两个指针从两边走向中间完成。并且前提是数组是有序的。
题目一:Two Sum
Given an array of integers, find two numbers such that they add up to a specific target number.
思路:啥都不说,排个序,while循环两个指针从分别从两头往中间走。
class Solution {
public:
vector<int> twoSum(vector<int> &numbers, int target) {
int i=0;
int j=numbers.size()-1;
vector<int> res;
vector<int> copy=numbers;
int first,second;
sort(copy.begin(),copy.end()); //第一步,排序
while(i<j){ //第二步,从两边往中间走
int sum=copy[i]+copy[j];
if(sum==target){
first=copy[i];
second=copy[j];
break;
}
else if(sum<target) i++;
else j--;
}
for(int i=0;i<numbers.size();i++){ //改变了原始顺序,得额外再搜索一遍
if(res.size()<2){
if(first==numbers[i]) res.push_back(i+1);
else if(second==numbers[i]) res.push_back(i+1);
}
}
return res;
}
};
说明:排序的话意味着改变了原始数据,不然的话可以牺牲额外的空间替换。最后需要增加一次额外的遍历,找到相应数据对应的index。还有一中替代方案,就是用map,一方面map是排序的,另一方面,map可以讲排序后的数据和排序前的index关联起来。
题目二:3Sum
Given an array S of n integers, are there elements a, b, c in S such that a + b + c=0? Find all unique triples in the array which gives the sum of zero. Note
- Element in a triplet (a, b, c) must be in non-descending order (ie. a<=b<=c)
- The solution set must not contain duplicate triplets.
class Solution {
public:
vector<vector<int> > threeSum(vector<int> &num) {
// IMPORTANT: Please reset any member data you declared, as
// the same Solution instance will be reused for each test case.
typedef vector<int> numbers;
vector<numbers> result;
sort(num.begin(), num.end());
for (int x = num.size()-1; x >=2; x--) { // Do it backwards
if(x < num.size()-1 & num[x] == num[x+1]) continue; // Skip same x
for (int y = 0, z = x-1; y < z;) {
if(y>0 && num[y]==num[y-1]) { // Skip same y
y++;
continue;
}
if(z<x-1&&num[z]==num[z+1]) { // Skip same z
z--;
continue;
}
int s = num[x] + num[y] + num[z];
if (s > 0)
z--;
if (s < 0)
y++;
if (s == 0) {
vector<int> tmpOne(3);
tmpOne[0] = num[y];
tmpOne[1] = num[z];
tmpOne[2] = num[x];
result.push_back(tmpOne);
y++;z--; //因为要找出所有的triplet,所有搜索到这里还不能结束
}
}
}
return result;
}
};
说明:代码中对x, y, z的枚举都进行了相应的重复屏蔽操作。代码总共两层循环,第一层循环是对sum可能性的枚举,第二个循环就是上面的2Sum的处理,这里也可以写成一个while循环。因为要找出所有的triplet,所以找到一个结果还得继续循环。
题目三:3Sum Closest
Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution. For example, given array S={-1 2 1 -4}, and target=1. The sum that is closest to the target is 2. (-1+2+1=2).
思路:这道题稍微要比上面的3Sum难。但还是需要sort一下。这次不是求确切的值,而是求一个近似解。
class Solution {
public:
int threeSumClosest(vector<int> &num, int target) {
double res;
double min=1<<12;
if(num.size()==0) return 0;
sort(num.begin(), num.end());
for(int k=num.size()-1;k>=2;k--){ //遍历第一个可能取的值
int i,j;
for(i=0,j=k-1; i<j;){ //2Sum的遍历
double tmp_sum=num[i]+num[j]+num[k];
double delta=abs(tmp_sum-target);
if(delta<min){
min=delta;
res=tmp_sum;
}
if(tmp_sum<target) i++;
else j--;
}
}
return (int)res;
}
};
题目四: 4Sum
Given an array S of n integers, are there elements a, b, c, and d in S such that a+b+c+d=target? find all unique quadruplets in the array which gives the sum of target. Note:
- Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a<=b<=c<=d)
- The solution set must not contain duplicate quadruplets
class Solution {
public:
vector<vector<int> > fourSum(vector<int> &num, int target) {
vector<int> res;
vector<vector<int> > collects;
int i,j;
if(num.size()==0) return collects;
sort(num.begin(), num.end());
for(int k1=num.size()-1;k1>=3;k1--){ //枚举第一个数
if(k1<num.size()-1&&num[k1]==num[k1+1]) //过滤掉重复的数
continue;
for(int k2=k1-1;k2>=2;k2--){ //枚举第二个数
if(k2<k1-1&&num[k2]==num[k2+1]) //过滤掉第二个数
continue;
for(i=0,j=k2-1; i<j;){
if(i>0 && num[i]==num[i-1]){
i++; continue;
}
if(j<k2-1 && num[j]==num[j+1]){
j--; continue;
}
int tmp_sum=num[i]+num[j]+num[k1]+num[k2];
if(tmp_sum==target){
res.push_back(num[i]);
res.push_back(num[j]);
res.push_back(num[k2]);
res.push_back(num[k1]);
collects.push_back(res);
res.clear();
i++;
j--;
}
else if(tmp_sum<target) i++;
else j--;
}
}
}
return collects;
}
};
下面继续扩大难度,在来一道附加题。
题目五:求和为s的序列
输入一个正整数s, 打印出所有和为s的连续正整数序列(至少含有两个数)。例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以结果打印出3个连续序列1~5、4~6和7~8.
解析:这次也同样是用两个index,i, j, 不过是另个指针往中间挪动,一个负责序列的头,一个复杂序列的尾部。
void findSequenceSum(int sum){
if(sum<3) return;
int i=1; //第一个index,指向地位
int j=2; //第二个index,指向高位
int mid=(1+sum)/2;
int curSum=i+j;
while(i<mid){
if(curSum==sum) print(i,j);
while(curSum>sum&&i<mid){
curSum-=i;
i++;
if(curSum==sum) print(i,j);
}
<span style="white-space:pre"> </span> j++;
curSum+=j;
}
}
void findSequenceSum2(int sum){
if(sum<3) return;
int i=1; //第一个index,指向地位
int j=2; //第二个index,指向高位
int mid=(1+sum)/2;
int curSum=i+j;
while(i<mid){
if(curSum==sum) print(i,j); //因为要找出所有的序列,故找到之后还得继续循环
if(curSum<=sum){
j++;
curSum+=j;
}
else{
curSum-=i;
i++;
}
}
}
注意,题目2要求找到所有符合要求的序列,故找到一个之后,还得让index继续往前探寻!
总结:(1)如果是求序列的和,用两个指针从左到右,一个负责头部start,一个负责尾部end。如果当前和<target, 则end++, 否则start++. (2)如果求几个数的和,不应定是序列,则用两个指针分别从两边往中间走。(3)总的特点是都需要排序。