在数组中找出3个数使得它们和为0

题目:

给定一个集合S,试找出3个数a,b,c,使得a+b+c=0。也即从集合中找出所有的和为0的3个数。
例如:集合S={-1,0,1,2,-1,4},则满足条件的3个数有2对: (-1, 0, 1)(-1, 2, -1)。注意(-1,1,0)与(-1,0,1)算同一个解,所以不用重复考虑。当然该例子集合的解也可以写成: (0, 1, -1)(2, -1, -1)
 

解法:

这个问题也被称作3数和问题,3数和问题是下面这个问题的扩展。
 
问题:给定一个n个元素的集合S,找出S中满足条件的整数对A,B, 使得A+B=K
 
假定集合S已经排好序的话,则上面这个问题可以在O(n)的时间内解决。使用2个索引值first和last,分别指向第一个元素和最后一个元素,设指向的第一个元素为A,则我们的任务就是找到对应于A的元素B,B=K-A。如果last指向的元素小于B,则first加1,指向后面的一个元素;如果last指向的元素大于B,则last减1。这样最终一步步逼近结果,时间复杂度为O(n)。该算法代码如下:
[cpp] view plain copy
 
  1. /*k为和,a为元素数组,n为数组大小*/
[cpp] view plain copy
 
  1. voidfindsum(intk,inta[],intn)
  2. {
  3. boolfound=false;
  4. sort(a,a+n);//对数组排序
  5. inti=0,j=n-1;
  6. while(i<j){
  7. if(a[i]+a[j]<k)//和小于K,则i++
  8. i++;
  9. elseif(a[i]+a[j]>k)//和大于K,则j--
  10. j--;
  11. else{//找到了,a[i]+a[j]=k
  12. cout<<"find"<<a[i]<<"+"
  13. <<a[j]<<"="<<k<<endl;
  14. i++;
  15. j--;
  16. found=true;
  17. }
  18. }
  19. if(!found)
  20. cout<<"notfound"<<endl;
  21. }
在上面这个解法的基础上,我们可以在O(n^2)的时间内解决3数和问题。这里稍有不同的是,上面问题的和K不一定是数组中的元素,它只是程序指定的一个参数。而在3数和问题中,如果转化为a+b=-c的问题,还需要保证-c在数组中。下面代码采用了两个循环,第一个循环代表初始值,即先是第一个值a[0]不变,计算a[0]+a[1]+a[n-1],若大于0则k减1,计算a[0]+a[1]+a[n-2],若小于0则j加1,计算a[0]+a[2]+a[n-1]...如果存在多个重复值,这可能会加入重复的数对,不过使用数据结构set可以解决该问题,相同的数对不会出现在set中。
[cpp] view plain copy
 
  1. set<vector<int>>find_triplets(vector<int>arr)
  2. {
  3. sort(arr.begin(),arr.end());
  4. set<vector<int>>triplets;
  5. vector<int>triplet(3);
  6. intn=arr.size();
  7. for(inti=0;i<n;i++){
  8. intj=i+1;
  9. intk=n-1;
  10. while(j<k){
  11. intsum_two=arr[i]+arr[j];
  12. if(sum_two+arr[k]<0){
  13. j++;
  14. }elseif(sum_two+arr[k]>0){
  15. k--;
  16. }else{
  17. triplet[0]=arr[i];
  18. triplet[1]=arr[j];
  19. triplet[2]=arr[k];
  20. triplets.insert(triplet);
  21. j++;
  22. k--;
  23. }
  24. }
  25. }
  26. returntriplets;
  27. }


猜你喜欢

转载自vergilwang.iteye.com/blog/2011221