我尝试了3sum problem的另一种方法:给定一个数组找到所有三元组总和到给定数字。
基本上,方法是:对数组进行排序。一旦选择了一对元素(比如A [i]和A [j]),就会对第三个元素[使用equal_range function]进行二分搜索。超过最后一个匹配元素的索引保存在变量“c”中。由于A [j + 1]> A [j],我们只搜索上下并排除索引c(因为索引c及以上的数字肯定会大于目标总和)。对于j = i + 1的情况,我们将结束索引保存为'd'而使c = d。对于i的下一个值,当j = i + 1时,我们需要仅搜索到并排除索引d。
C ++实现:
int sum3(vector<int>& A,int sum)
{
int count=0, n=A.size();
sort(A.begin(),A.end());
int c=n, d=n; //initialize c and d to array length
pair < vector<int>::iterator, vector<int>::iterator > p;
for (int i=0; i<n-2; i++)
{
for (int j=i+1; j<n-1; j++)
{
if(j == i+1)
{
p=equal_range (A.begin()+j+1, A.begin()+d, sum-A[i]-A[j]);
d = p.second - A.begin();
if(d==n+1) d--;
c=d;
}
else
{
p=equal_range (A.begin()+j+1, A.begin()+c, sum-A[i]-A[j]);
c = p.second - A.begin();
if(c==n+1) c--;
}
count += p.second-p.first;
for (auto it=p.first; it != p.second; ++it)
cout<<A[i]<<' '<<A[j]<<' '<<*it<<'\n';
}
}
return count;
}
int main() //driver function for testing
{
vector <int> A = {4,3,2,6,4,3,2,6,4,5,7,3,4,6,2,3,4,5};
int sum = 17;
cout << sum3(A,sum) << endl;
return 0;
}
我无法计算出此算法所需的上限时间。据我所知,最糟糕的情况是当目标金额无法实现时。
我的计算结果如下:
对于i = 0,没有。二进制搜索是lg(n-2)+ lg(n-3)+ ... + lg(1)
对于i = 1,lg(n-3)+ lg(n-4)+ ... + lg(1)
...
...
...
对于i = n-3,lg(1)
完全,lg((n-2)!)+ lg((n-3)!)+ ... + lg(1!) = lg(1 ^ n * 2 ^(n-1) 3 ^(n-2) ... *(n-1)^ 2 * n ^ 1)
但是如何从这个表达式中推导出O(n)约束?
答案 0 :(得分:2)
除了詹姆斯的好答案之外,我想指出,在最坏的情况下,这实际上可以达到O (n^3)
,因为你正在运行3个嵌套for循环。考虑案例
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
并且所需金额为3。
答案 1 :(得分:1)
在计算复杂性时,我将首先参考Big-O Cheat sheet。我使用这个表来对代码的较小部分进行分类,以获得它们的运行时性能。
E.g。如果我有一个简单的循环,它将是O(n)
。 BinSearch(根据备忘单)是O(log(n))
等等。
接下来,我使用Properties of Big-O notation将较小的部分组合在一起。
因此,例如,如果我有两个彼此独立的循环,它将是O(n) + O(n)
或O(2n) => O(n)
。如果我的一个循环在另一个循环中,我会乘以它们。因此g( f(x) )
变为O(n^2)
。
现在,我知道你在说:&#34;嘿,等等,我正在改变内环的上限和下限&#34;但我认为这不重要...... here's a university level example。
因此,我对您的运行时的后期计算是O(n^2) * O(Log(n))
或O(n^2 Log(n))
。
但事实并非如此。我可以做一些可怕的错误。所以我的下一步是开始绘制最坏情况的运行时。将sum设置为不可能的大值并生成越来越大的数组。您可以通过使用大量重复的较小数字来避免整数溢出。
另外,将其与Quadratic 3Sum Solution进行比较。这是一个已知的O(n^2)
解决方案。一定要比较最坏的情况,或两者上至少相同的数组。同时执行两个定时测试,以便在您根据经验测试运行时时开始感觉哪个更快。
发布版本,针对速度进行了优化。
答案 2 :(得分:0)
1。要进行分析,请注意
log(1) + log(2) + ... + log(k) = Theta(k log(k))
。
实际上,这个总和的上半部分是log(k / 2)+ log(k / 2 + 1)+ ... + log(k),
所以它至少是log(k / 2)* k / 2,它渐渐与log(k)* k相同。
同样,我们可以得出结论
log(n-1) + log(n-2) + log(n-3) + ... + log(1) + // Theta((n-1) log(n-1))
log(n-2) + log(n-3) + ... + log(1) + // Theta((n-2) log(n-2))
log(n-3) + ... + log(1) + // Theta((n-3) log(n-3))
... +
log(1) = Theta(n^2 log(n))
实际上,如果我们考虑至少为log(n / 2)的对数,它就是左上象限的半三角形(因此~1 / 2)(因此~n ^ 2/4) )上述总和,所以有Theta(n ^ 2/8)这样的术语。
2. 正如satvik在另一个答案中所指出的,当输出数量本身为Theta(n ^ 3)时,输出循环可能需要达到Theta(n ^ 3)步长,这是当他们都平等时。
3。三和问题有O(n ^ 2)个解,因此渐近地快于这个。