此道题目跟3Sum很相似,用的方法也是一样的。时间复杂度为O(N^3)。代码如下:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int len = (int)nums.size(), i, j, remain;
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (i=0; i<len-3; i++) {
if (i != 0 && nums[i] == nums[i-1]) { /<span style="color:#ff0000;">/ 去除重复解法</span>
continue;
}
for (j=i+1; j<len; j++) {
if (j != i+1 && nums[j] == nums[j-1]) { <span style="color:#ff0000;">// 去除重复解法</span>
continue;
}
remain = target-(nums[i]+nums[j]);
int p = j+1, q = len-1;
while (p < q) {
if (nums[p] + nums[q] < remain) {
p++;
} else if (nums[p] + nums[q] > remain) {
q--;
} else {
vector<int> tmp;
tmp.push_back(nums[i]);
tmp.push_back(nums[j]);
tmp.push_back(nums[p]);
tmp.push_back(nums[q]);
result.push_back(tmp);
p++;
q--;
while (p<q && nums[p] == nums[p-1]) { <span style="color:#ff0000;">// 去除重复解法
</span> p++;
}
while (p<q && nums[q] == nums[q+1]) { <span style="color:#ff0000;">// 去除重复解法</span>
q--;
}
}
}
}
}
return result;
}
这道题目据说有O(N^2)的解法,我在网上搜寻了很长时间,也没用找到十分完美的O(N^2)解法。网上大多数O(N^2)的解法都是错误的。找到的稍微靠谱一点的勉强算得上O(N^2)的解法如下:先对数组排序。我们先枚举出所有二个数的和存放在哈希map中,其中map的key对应的是二个数的和,因为多对元素求和可能是相同的值,故哈希map的value是一个链表(下面的代码中用数组代替),链表每个节点存的是这两个数在数组的下标;这个预处理的时间复杂度是O(n^2)。接着和算法1类似,枚举第一个和第二个元素,假设分别为v1,v2, 然后在哈希map中查找和为target-v1-v2的所有二元对(在对应的链表中),查找的时间为O(1),为了保证不重复计算,我们只保留两个数下标都大于V2的二元对(其实我们在前面3sum问题中所求得的三个数在排序后的数组中下标都是递增的),即时是这样也有可能重复:比如排好序后数组为-9 -4 -2 0 2 4 4,target = 0,当第一个和第二个元素分别是-4,-2时,我们要得到和为0-(-2)-(-4) = 6的二元对,这样的二元对有两个,都是(2,4),且他们在数组中的下标都大于-4和-2,如果都加入结果,则(-4,-2,2,4)会出现两次,因此在加入二元对时,要判断是否和已经加入的二元对重复(由于过早二元对之前数组已经排过序,所以两个元素都相同的二元对可以保证在链表中是相邻的,链表不会出现(2,4)->(1,5)->(2,4)的情况,因此只要判断新加入的二元对和上一个加入的二元对是否重复即可),因为同一个链表中的二元对两个元素的和都是相同的,因此只要二元对的一个元素不同,则这个二元对就不同。我们可以认为哈希map中key对应的链表长度为常数,那么算法总的复杂度为O(n^2)。代码如下:
vector<vector<int> > fourSum(vector<int> &num, int target) {
int n = num.size();
vector<vector<int> > res;
unordered_map<int, vector<pair<int, int> > >pairs; // hash map
pairs.reserve(n*n); // 设定hash map中桶的数量
sort(num.begin(), num.end());
for(int i = 0; i < n; i++)
for(int j = i+1 ; j < n; j++)
pairs[num[i]+num[j]].push_back(make_pair(i,j)); // 注意make_pair(i, j)这种写法
for(int i = 0; i < n - 3; i++)
{
if(i != 0 && num[i] == num[i-1])continue;//防止第一个元素重复
for(int j = i+1; j < n - 2; j++)
{
if(j != i+1 && num[j] == num[j-1])continue;//防止第二个元素重复
if(pairs.find(target - num[i] - num[j]) != pairs.end())
{
vector<pair<int, int>> &sum2 = pairs[target - num[i] - num[j]];
bool isFirstPush = true;
for(int k = 0; k < sum2.size(); k++)
{
if(sum2[k].first <= j)continue;//保证所求的四元组的数组下标是递增的
if(isFirstPush || (res.back())[2] != num[sum2[k].first]) // 判断是否和上一个二元对重复
{
res.push_back(vector<int>{num[i], num[j], num[sum2[k].first], num[sum2[k].second]});
isFirstPush = false;
}
}
}
}
}
return res;
}