如果数组是无序的,先排序(n*logn),然后用两个指针i,j,各自指向数组的首尾两端,令i=0,j=n-1,然后i++,j--,逐次判断a[i]+a[j]?=sum,如果某一刻a[i]+a[j]>sum,则要想办法让sum 的值减小,所以此刻i 不动,j--,如果某一刻a[i]+a[j]<sum,则要想办法让sum 的值增大,所以此刻i++,j 不动。所以,数组无序的时候,时间复杂度最终为O(n*logn+n)=O(n*logn),若原数组是有序的,则不需要事先的排序,直接O(n)搞定,且空间复杂度还是O(1),此思路是相对于上述所有思路的一种改进。
Pair findSum(int *s,int n,int x) { //sort(s,s+n); 如果数组非有序的,那就事先排好序O(N*logN) int *begin=s; int *end=s+n-1; while(begin<end) //俩头夹逼,或称两个指针两端扫描法,很经典的方法,O(N) { if(*begin+*end>x) { --end; } else if(*begin+*end<x) { ++begin; } else { return Pair(*begin,*end); } 178 } return Pair(-1,-1); }
vector<vector<int> > threeSum(vector<int> &num) { if(num.empty()) return vector<vector<int> >(); sort(num.begin(),num.end()); vector<vector<int> > ret; vector<int> tmp; int n=num.size(); for(int i=0;i<n-2;i++) { if(i>0&&num[i]==num[i-1]) continue;//防止存在重复的元素 int target=-num[i]; int j=i+1; int k=n-1; while(j<k) { if(j<k&&k<n-1&&num[k]==num[k+1]) { k--; continue; } if(num[j]+num[k]==target) { tmp={num[i],num[j],num[k]}; ret.push_back(tmp); j++; k--; } else if(num[j]+num[k]<target) { j++; } else if(num[j]+num[k]>target) k--; } } return ret; }
vector<vector<int> > fourSum(vector<int> &num,int target) { if(num.empty()) return vector<vector<int> >(); sort(num.begin(),num.end()); vector<vector<int> > ret; int n=num.size(); int i,j; for(i=0; i<n-3; i++) { //只保留第一个不重复的,其余的都删了,因为left会选择重复的 if(i>=1&&num[i]==num[i-1]) continue; for(j=n-1; j>i+2; j--) { //只保留最后一个不重复的,其余的都删了,因为right会选择重复的 if(j<n-1&&num[j+1]==num[j]) continue; int left=i+1; int right=j-1; vector<int> tmp; while(left<right) { //只保留最后一个不重复的,其余的都删了,因为left会选择重复的 if(right<j-1&&num[right]==num[right+1]) { right--; continue; } if(num[i]+num[j]+num[left]+num[right]==target) { tmp= {num[i],num[left],num[right],num[j]}; ret.push_back(tmp); left++; right--; } else if(num[i]+num[j]+num[left]+num[right]<target) left++; else if(num[i]+num[j]+num[left]+num[right]>target) right--; } } } return ret; }
void sum(int sum,int n) { if(sum<0||n<2) return -1; int cursum=0; cursum=1+2; int i=0,j=1; while(j<=n) { if(cursum==sum) { int k=i; while(k<=j) { cout<<k<<' '; k++; } cout<<endl; cursum-=i; i++; } else if(cursum<sum) { j++; cursum+=j; } else if(cursum>sum) { cursum-=i; i++; } } }
用回溯的方法实现:
#include<iostream> #include<vector> using namespace std; void findhelper(int m,int n,int start,vector<int> &path) { if(m<0) return; if(m==0) { for(auto a:path) cout<<a<<' '; cout<<endl; return; } for(int i=start; i<=n; ++i) { path.push_back(i); findhelper(m-i,n,i+1,path); path.pop_back(); } } void findSum(int m,int n) { vector<int> path; findhelper(m,n,1,path); } int main() { findSum(15,20); }
用0-1背包法实现:
注意到取n,和不取n个区别即可,考虑是否取第n个数的策略,可以转化为一个只和前n-1个数相关的问题。
实现代码:
#include<iostream> #include<vector> using namespace std; void findhelper(int sum,int n,vector<int> &path) {
//递归出口 if(sum<=0||n<=0) return;
//输出找到的结果 if(sum==n) { for(int i=path.size()-1; i>=0; --i) cout<<path[i]<<' '; cout<<n<<endl; } path.push_back(n); //典型的01背包问题 findhelper(sum-n,n-1,path);//放第n个元素 path.pop_back(); findhelper(sum,n-1,path); //不放第n个元素 } void findSum(int m,int n) { vector<int> path; findhelper(m,n,path); } int main() { findSum(15,20); }
#include<iostream> #include<vector> #include<algorithm> using namespace std; void helper(vector<int> &num,vector<int> &path,int start,int sum) { if(sum==0) { for(int i=0;i<path.size();++i) cout<<path[i]<<' '; cout<<endl; return; } if(sum<0) return; for(int i=start;i<num.size();++i) { if(i>start&&num[i]==num[i-1]) continue; path.push_back(num[i]); helper(num,path,i+1,sum-num[i]); path.pop_back(); } } void findSum(vector<int> &num,int sum) { vector<int> path; sort(num.begin(),num.end()); helper(num,path,0,sum); } int main() { vector<int> num={1,2,1,3,0,1}; findSum(num,3); }