三和替代方法

时间:2018-06-09 10:14:43

标签: c++ algorithm array-algorithms

我尝试了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)约束?

3 个答案:

答案 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)个解,因此渐近地快于这个。