该算法查找所有组合的时间复杂度是多少?

Posted

技术标签:

【中文标题】该算法查找所有组合的时间复杂度是多少?【英文标题】:What's time complexity of this algorithm for finding all combinations? 【发布时间】:2014-08-29 20:53:00 【问题描述】:

组合 给定两个整数 n 和 k,返回 1 ... n 中 k 个数字的所有可能组合。例如,如果 n = 4 和 k = 2,则解为:

[
   [2, 4],
   [3, 4],
   [2, 3],
   [1, 2],
   [1, 3],
   [1, 4],
]


个人认为,时间复杂度= O(n^k),输入n和k。
感谢大家的帮助。最后,时间复杂度 = O(C(n,k) * k) = O((n!/(k! * (n - k)!)) * k), n 和 k 是输入, 因为,每次我们得到一个组合时,我们都需要将 subList 列表复制到 one_rest,也就是 O(k), 有 C(n, k) * k。
C++
#include <vector>
using namespace std;

class Solution 
public:
    vector<vector<int> > combine(int n, int k) 
        vector<vector<int>> list;

        // Input validation.
        if (n < k) return list;

        int start = 1;
        vector<int> subList;
        helper(n, k, start, list, subList);

        return list;
    

    void helper(int n, int k, int start, 
                vector<vector<int>> &list, vector<int> &subList) 
        // Base case.
        if (subList.size() == k) 
            vector<int> one_rest(subList);
            list.push_back(one_rest);
            return;
        
        if (start > n) return;

        for (int i = start; i <= n; i ++) 
            // Have a try.
            subList.push_back(i);

            // Do recursion.
            helper(n, k, i + 1, list, subList);

            // Roll back.
            subList.pop_back();
        
    
;

【问题讨论】:

【参考方案1】:

复杂度为O(C(n,k)),即O(n choose k)

这最终等同于O(min(n^k, n^(n-k)))

【讨论】:

怎么可能是 O(min(n^k, n^(n-k)))? n=11k=8n-k = 3 然后11 choose 8 = 16511 choose 3 = 165。你会发现n choose k 对应的k &lt;= n / 2O(N^k)。而对于k &gt; n / 2,公式为O(n^(n-k))。你可以把它写成分段函数,基本上就是我刚才在那里描述的,或者你可以简单地说O(min(n^k, n^(n-k))。如果您查看有关如何针对 k 的特定值计算 n choose k 的公式,这一点会变得更加清晰。 虽然 C(n,k) 确实是 O(n^k) 的一个元素,但如果 k 很大,这是一个非常宽松的上限;例如,C(n, n/2) < 2^n @Stef 那是我有min 在那里。您使用较小的 O(n^k)O(n^(n-k))。如果k &gt;&gt; n/2 然后O(n^(n-k)) 非常小,应该使用它。可能有一种不需要min() 的方式来重写它,但即使现在也想不到。 @Nuclearman 我应该说“如果 k 接近 n/2”而不是“如果 k 很大”。我很清楚 C(n, k) = C(n, n-k)。这不是我要批评的。我要说的是 n^k 是一个非常松散的界限,除非 k 非常接近 0 或 n。极端情况是 k = n/2:在这种情况下,C(n, k) 小于 2^n,但 n^k = sqrt(n)^n 远大于 2^n。【参考方案2】:

由于您使用的是列表,push_backpop_backO(1) 操作。此外,您最终只生成了一次有效组合。因此,复杂度为O(n choose k)

【讨论】:

你能用O(n choose k)这样的组合来写复杂性,还是你必须提供最终的等价。您能否指出我对此的参考。 f(x) = O(g(x)) 是对f(x)g(x) 的限制行为的严格陈述。见here。说O(n choose k) 没有错。但是,根据您的情况,您可能需要不同的 g(x)。例如,如果您尝试将 O(n choose k) 算法与其他一些指数复杂度算法进行比较,您可能希望将 n choose k 替换为使用 Stirling's approximation 的算法。【参考方案3】:

时间复杂度等于组合的数量。

在本例中为n choose k

【讨论】:

+1,简单而明显的答案,因为算法必须生成 all 组合,而这些(对于给定的 n,k)已知为 $n 选择 k $ 这也是算法的时间复杂度(否则算法会产生有偏差的组合,要么是缺失要么是重复)【参考方案4】:

我认为不是 O(n^k)。因为想想。让我们假设 n=100 和 k=2。根据您的复杂性,它将是 100 的 2 次方。但是如果它是 n=100 且 k=10,它将是 100 的 10 次方。但是如果您考虑一下,n=100 的组合要多得多,k=2 比 n=100,k=10。复杂性实际上是实际公式:即 n!/(k!(n-k)!)。因此复杂度将是 O(n!/k!(n-k)!)。

【讨论】:

这没有必要矛盾。 Big-O 表示法指定了上限,因此特定的数字示例不太适用于它。 n!/k!(n-k)! 是实际的精确计算,因此可以作为复杂性的最严格边界。但是,答案取决于问题的定义。如果kn 都是变量,那么复杂度就是O(n!/k!(n-k)!)。但是如果 k 是常量 - n!/k!(n-k)!=O(n!/k!(n-k)!)=O(n^k) 您能否详细说明一下您是如何通过 K 是恒定的事实获得 O(n^k) 的?我无法跟随你。 @vinicius.olifer 在这里回复旧评论,但可能对未来的人有用。 O(n!/k!(n-k)!) 是最准确的定义,但在大多数情况下讨论大 O 符号时并不是非常有用。阶乘只是增加了通常不需要的复杂性。出于更实际的目的,我上面的答案使用O(min(n^k, n^(n-k)))。由于 O 仅表示“不比”更差,因此可以使用这两者中的较高者并仅使用 O(n^k),但如果 k &gt;= n/2 更准确地说是 O(n^(n-k))

以上是关于该算法查找所有组合的时间复杂度是多少?的主要内容,如果未能解决你的问题,请参考以下文章

在 MongoDB 中,查找所有行 ID 不在给定列表中的记录的查询时间复杂度是多少?

Day 18:二分查找算法

题目3. 平衡二叉树算法查找树中某节点的时间复杂度是多少?

算法初步:二分查找

ac自动机时间复杂度是多少?

该算法的比特成本时间复杂度是多少(Java 示例)?