Leetcode中一维数组与xSum

在一维数组总求各种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.
思路:首先第一点,要求triplet有序,很明显要将原始数据(或者备份数据)排序。第二点,去掉重复。所有企图先将重复triplet加进来再去掉都是幻想,我以身试法多次均失败告终,虽然知道是很蠢的方法,但还不让我过。。所以只能在遍历的时候对重复数据进行严格把关了。虽然3Sum比2Sum复杂,但是3Sum要求的只是和为0,那么是不是针对每个负数或0,求一个2Sum算法就OK了呢?貌似可行,但是不行,因为不是所有三元组都是由负数+2个非负数,有可能是2个负数+1一个整数组成,那么这种想法算是失败。失败的原因在于我们只考虑了将负数或者0作为sum,而事实是所有元素都可能成为sum。

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
思路:这道题的难度比前面的增大了,要枚举四个数。不管枚举几个数,还是得先排序吧。直觉的解法是先枚举前两个,后进行2Sum。这里也要去重复。

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)总的特点是都需要排序。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值