算法题: 三数之和的多种可能
这是本人第一次写博客,之前总结一些内容都是使用印象笔记,后来觉得笔记这个东西自己记了有的时候很久才会去看,可能根本不会回去再看,索性把自己积累知识的过程写成blog,这样你们也可以借鉴参考,何乐而不为呢!
题目详情:(来源LeedCode)
给定一个整数数组 A,以及一个整数 target 作为目标值,返回满足 i < j < k 且 A[i] + A[j] + A[k] == target 的元组 i, j, k 的数量。
由于结果会非常大,请返回 结果除以 10^9 + 7 的余数。
示例 1:
输入:A = [1,1,2,2,3,3,4,4,5,5], target = 8
输出:20
解释:
按值枚举(A[i],A[j],A[k]):
(1, 2, 5) 出现 8 次;
(1, 3, 4) 出现 8 次;
(2, 2, 4) 出现 2 次;
(2, 3, 3) 出现 2 次。
示例 2:
输入:A = [1,1,2,2,2,2], target = 5
输出:12
解释:
A[i] = 1,A[j] = A[k] = 2 出现 12 次:
我们从 [1,1] 中选择一个 1,有 2 种情况,
从 [2,2,2,2] 中选出两个 2,有 6 种情况。
该题目一上来,本人就一顿递归暴力求解,先看看让我死的比较惨的代码吧(第一次用不会使用高亮):
//递归函数
void count_group(vector<int>& A, int target, int& sum, int count, int index)
{
if (target == 0 && count == 3)
{
sum++;
sum = sum%(1000000007);
return;
}
//不满足条件直接返回
if (target < 0 || count > 3
|| (count == 3 && target > 0) || index >= A.size())
return;
//选择当前index
target -= A[index];
count++;
count_group(A, target, sum, count, index + 1);
//不选择当前的index
target += A[index];
count--;
count_group(A, target, sum, count, index + 1);
}
//主函数
int threeSumMulti(vector<int>& A, int target) {
if (A.size()<3 || target<0)
return 0;
int sum = 0;
int count = 0, index = 0;
sort(A.begin(), A.end());
count_group(A, target, sum, count, index);
return sum;
}
这个方法,比较容易理解,就是通过F(n, target) = F(n,target - n(index)) + F(n, target)来进行递归的,这个式子的意思是当前所求的组数 = 选择当前元素所求的组数 + 不选择当前元素所求的组数。
问题是: 复杂度过高复杂度在特殊情况下达到了 n!。
示例:
300 个 0;
0;
该程序采用递归运行了多达半个小时都没有出来结果,最后担心内存不够就停止了程序,所以该程序只完成了将近50%的测试用例。下面介绍一种比较直接求解方法:
非递归枚举求解:
int threeSumMulti(vector<int>& A, int target) {
const long long M = pow(10, 9) + 7;
// 复杂度O(n^2)
sort(A.begin(), A.end());
long long res = 0;
for (int k = 0; k + 2<A.size(); k++) {
// 第一个数为A[k]
long long i = k + 1, j = A.size() - 1;
while (i<j) {
if (A[i] + A[j]>(target - A[k])) {
j--;
}
else if (A[i] + A[j] == (target - A[k])) {
if (A[i] == A[j]) {
// 求C(j-i+1,2)
res = (res + (j - i + 1)*(j - i) / 2) % M;
break;
}
else {
//求出i,j对应的元素个数乘积
int ti = i, tj = j;
while (ti + 1<tj && A[ti] == A[ti + 1]) ti++;
while (ti<tj - 1 && A[tj] == A[tj - 1]) tj--;
res = (res + (ti - i + 1)*(j - tj + 1)) % M;
i = ti; i++;
j = tj; tj--;
}
}
else {
i++;
}
}
}
return res;
}
该算法复杂度为n2,可以完美AC