这类问题,要先将数组进行排序,然后用夹逼法则(首尾指针)。最终都可以化解为2sum的问题,但是要注意过程中的去重步骤。
leetcode题目:
2SUM
思路一:将数组排好序后,使用前后指针。时间复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),主要在于排序的时间。
思路二:建立map
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
map<int, int> m;
vector<int> ans;
for(int i=0;i<nums.size();i++){
if(m.count(target-nums[i])!=0){
ans.push_back(m[target-nums[i]]);
ans.push_back(i);
return ans;
}
else{
m[nums[i]]=i;
}
}
return ans;
}
};
// 这道题需要返回下标 因此不采用排序了
3SUM
思路:通过外层循环确定一个数,然后剩下的就是寻找和为target-nums[i]的2SUM问题。
难点是如何去重,详见下面代码的注释。时间复杂度是
O
(
n
2
)
O(n^2)
O(n2)。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
int n = nums.size();
vector<vector<int>> ans;
int target, left, right;
for(int i=0;i<n;i++){
if(i>0 && nums[i]==nums[i-1]) // 为了去重
continue;
if( nums[i]>0 ) break; // 此时没有必要再进行下去了
target = 0-nums[i];
left = i+1;
right = n-1;
while(left < right){
if(nums[left]+nums[right]==target){
vector<int> tmp;
tmp.push_back(nums[i]);
tmp.push_back(nums[left]);
tmp.push_back(nums[right]);
ans.push_back(tmp);
// 为了去重
int val1 = nums[left];
while(left < right && nums[left]==val1)
left++;
int val2 = nums[right];
while(left < right && nums[right]==val2)
right--;
}else if(nums[left]+nums[right]>target){
right--;
}else {
left++;
}
}
}
return ans;
}
};
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int n = nums.size();
sort(nums.begin(),nums.end());
int ans = nums[0]+nums[1]+nums[2]; // 先给ans赋一个初始值
int left,right;
for(int i=0;i<n;i++){
if(i>0 && nums[i]==nums[i-1]) // 不想做没有必要的计算
continue;
left = i+1;
right = n-1;
while(left<right){
if( nums[left]+nums[right]+nums[i]==target){
return target;
}
if( abs(nums[left]+nums[right]+nums[i]-target) < abs(ans-target))
ans = nums[left]+nums[right]+nums[i];
if(nums[left]+nums[right]+nums[i]>target){
right--;
}else {
left++;
}
}
}
return ans;
}
};
class Solution {
public:
int threeSumMulti(vector<int>& A, int target) {
int n = A.size();
sort(A.begin(),A.end());
int left, right;
int mod = 1e9+7;
int ans = 0;
for(int i=0;i<n;i++){
left = i+1;
right = n-1;
while(left < right){
if(A[i]+A[left]+A[right]==target){
if(A[left]==A[right]){
ans += ((right-left)*(right-left+1)/2)%mod;
ans = ans%mod;
break;
}else {
left++;
right--;
int cnt1=1,cnt2=1;
while(left<=right && A[left]==A[left-1]){
left++;
cnt1++;
}
while(left<=right && A[right]==A[right+1]){
right--;
cnt2++;
}
ans += (cnt1*cnt2)%mod;
ans = ans%mod;
}
}else if(A[i]+A[left]+A[right]>target){
right--;
}else {
left++;
}
}
}
return ans;
}
};
4SUM
思路一:通过循环遍历确定第一个数,然后解决剩下的3SUM问题。时间复杂度
O
(
n
3
)
O(n^3)
O(n3)。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int n= nums.size();
sort(nums.begin(),nums.end());
vector<vector<int>> ans;
int left, right;
for(int i=0;i<n;i++){
if(i>0 && nums[i]==nums[i-1]) // 去重
continue;
for(int j=i+1;j<n;j++){
if(j>i+1 && nums[j]==nums[j-1]) // 去重
continue;
left = j+1;
right = n-1;
while(left < right){
if( nums[i]+nums[j]+nums[left]+nums[right]==target){
vector<int> tmp;
tmp.push_back(nums[i]);
tmp.push_back(nums[j]);
tmp.push_back(nums[left]);
tmp.push_back(nums[right]);
ans.push_back(tmp);
int val1 = nums[left];
int val2 = nums[right];
while(left<right && nums[left]==val1) // 去重
left++;
while(left<right && nums[right]==val2) // 去重
right--;
} else if (nums[i]+nums[j]+nums[left]+nums[right] > target){
right--;
}else{
left++;
}
}
}
}
return ans;
}
};
思路二:空间换时间,找出两个数的所有可能的和,时间复杂度是 O ( n 2 ) O(n^2) O(n2)。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
typedef pair<int,int> pii;
unordered_map<int, vector<pii>> m;
sort(nums.begin(),nums.end());
int n = nums.size();
for(int i=0;i<n;i++){
for(int j=i+1; j<n ;j++)
m[nums[i]+nums[j]].push_back(make_pair(i,j));
}
set<vector<int>> s;
vector<vector<int>> ans;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
int t = target-nums[i]-nums[j];
if( m.count(t)!=0){
for(int k=0; k< m[t].size();k++){
if(m[t][k].first > j){
vector<int> tmp{nums[i],nums[j],nums[m[t][k].first],nums[m[t][k].second] };
s.insert(tmp);
}
}
}
}
}
set<vector<int>>:: iterator iter = s.begin();
while(iter!=s.end()){
ans.push_back(*iter);
iter++;
}
return ans;
}
};
class Solution {
public:
int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
unordered_map<int, int> m;
int nA = A.size();
int nB = B.size();
int nC = C.size();
int nD = D.size();
for(int i=0;i<nA;i++){
for(int j=0;j<nB;j++)
m[A[i]+B[j]]++;
}
int cnt = 0 ;
for(int i=0;i<nC;i++){
for(int j=0; j<nD; j++){
if( m.count(0-C[i]-D[j])!=0){
cnt += m[0-C[i]-D[j]];
}
}
}
return cnt;
}
};
K SUM
https://segmentfault.com/a/1190000004984393
https://www.lintcode.com/problem/k-sum/description
class Solution {
public:
/**
* @param A: An integer array
* @param k: A positive integer (k <= length(A))
* @param target: An integer
* @return: An integera
*/
int kSum(vector<int> &A, int k, int target) {
// write your code here
int n = A.size();
int dp[n+1][k+1][target+1];
for(int i=0;i<=n;i++){
for(int j=0;j<=k;j++){
for(int h=0;h<=target;h++)
dp[i][j][h]=0;
}
}
for(int i=0; i< n+1; i++)
dp[i][0][0]=1;
for(int i=1; i< n+1; i++){
for(int j=1; j<=k && j<=i ; j++){
for(int h=1; h<=target;h++){
dp[i][j][h] = dp[i-1][j][h];
if( A[i-1]<=h)
dp[i][j][h] += dp[i-1][j-1][h-A[i-1]];
}
}
}
return dp[n][k][target];
}
};
[1] https://blog.youkuaiyun.com/haolexiao/article/details/70768526
[2] https://www.cnblogs.com/hankunyan/p/9881230.html