Letcode上面有几道关于K sum的问题,做起来感觉还挺麻烦,花了挺长时间做了一个适用于任意个k的通用版本,在这里总结一下,如果有更好的方法,还请不吝赐教。
问题描述:
Two Sum
Given an array of integers, find two numbers such that they add up to a specific target number.
Three Sum
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
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.
解题思路:
1. 暴力枚举,相信这是每个人都能想到的办法,时间复杂度是随着k的增长而指数增长O(N^k)
2.利用排序。
首先讨论k = 2的情况,
k = 2时,使用两个指针low and high,一个指向头,一个指向尾。找sum 为target的pair, 如果sum == target,记录下该对pair, low++; 如果sum < target, low++; 如果 sum >target high--. 直到 high == low, 时间复杂度为排序时间加线性遍历时间 O(nlogn + n),所以即为(nlogn)
接下来分析k > 2的情况,
这里可以用到递归解决子问题的思想,我们可以将k = 3 找寻 target 的问题 看成 k = 2 找寻 target - num[i] 的问题,即每次假设某一元素num[i] 在结果中,在剩余数组元素中,找寻满足 sum = target - num[i] 的元素pair。
该思想可以推广到k为任意的情况。时间复杂度我不是特别确定,因为当问题规模规约到 k = 2时,时间复杂度为O(nlogn),所以整体时间复杂度应该是O(n^(k-1))。如果有错误,欢迎指出
算法思想大概就是如上所述,但是还有很多实现细节问题,
1) 去重,题目要求我们返回一个vector<vector<int> > ,其中按non-decreasing 且不能有重复的结果。我看到网上很多人讨论这个问题,用到什么map,set... 感觉很麻烦而且完全没必要,简单分析可知,数组已经是有序,出现重复结果无非是连续相同元素所致,例如, 3 3 7 7 7 ,如果我们已经得到了以第一个7开头的正确结果vector, 那么我们不需要接下来 以 7 开头的任何结果vector,因为在解第一个以7开头的结果vector时,我们已经得到了所有以7开头的正确结果,那么这时我们应该大胆跳过。
所以,在扫描时,我们添加一个判断条件,只有当前一个元素和当前元素不相同时,或者是第一个元素(之前没有元素) ,我们才执行求解。
上CODE
vector<vector<int> > myfunction(vector<int>& num, int target, int k, int begin){
if(k == 2){
map<int,bool> existed;
vector<vector<int> > ret;
int low = begin;
int high = num.size() - 1;
while(low < high){
int tempsum = num[low] + num[high];
if(tempsum == target){
if(existed.find(num[low]) == existed.end()){
existed[num[low]] = true;
vector<int> tempret;
tempret.push_back(num[low]);
tempret.push_back(num[high]);
ret.push_back(tempret);
}
++low;
}else if(tempsum < target)++low;
else
--high;
}
return ret;
}else{
vector<vector<int> > cot;
if(num.size()-begin < k)return cot;
for(int i = begin; i < num.size() - (k - 1); ++i){
if(i == begin || num[i] != num[i-1]){
vector<vector<int> > ret = myfunction(num,target - num[i], k - 1, i + 1);
for(int p = 0; p < ret.size(); ++p){
vector<int> tempret;
tempret.push_back(num[i]);
for(int q = 0; q < ret[p].size(); ++q){
tempret.push_back(ret[p][q]);
}
cot.push_back(tempret);
}
}
}
return cot;
}
}
vector<vector<int> > ksum(vector<int>& num, int target, int k){
sort(num.begin(),num.end());
return myfunction(num,target,k,0);
}