如何确定这段代码的时间复杂度?

Posted

技术标签:

【中文标题】如何确定这段代码的时间复杂度?【英文标题】:How to determine time complexity of this code? 【发布时间】:2017-02-09 15:55:44 【问题描述】:

我刚刚开始学习算法,

int findMinPath(vector<vector<int> > &V, int r, int c)
    int R = V.size();
    int C = V[0].size();
    if (r >= R || c >= C) return 100000000; // Infinity
    if (r == R - 1 && c == C - 1) return 0;
    return V[r][c] + min(findMinPath(V, r + 1, c), findMinPath(V, r, c + 1));

我认为答案应该是 O(RC) 但正确答案是 O(2^(RC)) 我不明白为什么。请解释一下。

【问题讨论】:

它是双重递归的。如果将调用映射为图形,则会得到二叉调用树。不过,该算法对我来说没有意义。 嗯。不是 O((C+R) 选择 C),因为代码本质上是枚举从 (0,0) 到 (C,R) 的路径。 ?我认为这是 O(2^(RC)) ,但这是一个非常糟糕的上限。例如,当R=C时,(C+R)选择C约为4^R/sqrt(pi*R),比2^(R^2)增长慢很多。 也许问题中有一个错字:O(2^(R+C)) 而不是 O(2^(RC))?搜索树的深度最多为 R+C,因此 2^(R+C) 是复杂度的合理第一近似值。当然是 R+C=O(RC),所以这个问题在理论上也是正确的,但没有多大实际意义(至少对我来说)。 @Cheersandhth.-Alf 该算法是有意义的——它找到了从 (0,0) 到 (R,C) 的最低成本路径,其中所有步骤都正确或向上。当然,这不是编写代码的最佳方式,但我认为这是开始的“糟糕”算法,将通过动态编程加以改进。 哦,V[0].size() 让我失望了。但显然它是一个表示为向量向量的矩阵,而不是一般的锯齿状数组。所以,列和行。嗯。好的。 【参考方案1】:

当您有多个递归调用时,一个很好的近似值是分支(调用)的数量提升到“树”高度的幂。

在你的情况下,你有两个分支:

findMinPath(V, r+1, c) 
findMinPath(V, r, c+1)

所以我们将从 2 开始。

那么“树”的高度(或深度)取决于向量中有多少元素;在您的情况下,您有 R 元素,但每个元素中都有 C 元素。所以我们的力量是RC。

因此,您的运行时间将接近,在最坏的情况下为 O(2^RC)。

【讨论】:

树的最大深度是 R+C 而不是 RC 不是吗?到(R,C)的初始距离为R+C,每次递归调用距离减1。 @PaulHankin 我认为这是因为在最坏的情况下 RC 将遍历所有元素,因此正如 Cruiser 解释的那样,它应该是 2^(RC) 在最坏的情况下,在单个路径中,遍历的是 R+C(或者可能是 R+C-2,但这对复杂性没有影响)元素,而不是 RC 元素。向量中有 RC 元素,但您在网格中的搜索路径的步长仅在 (+1, 0) 和 (0, +1) 方向。【参考方案2】:

最坏的情况,findMinPath 在这行调用自己两次

return V[r][c] + min(findMinPath(V, r + 1, c), findMinPath(V, r, c + 1));

因此,最坏的情况是,每次调用都将涉及两个进一步的调用,直到递归结束。因此,二次幂。

【讨论】:

为什么是 2^(RC) 而不是,例如 2^(R+C)。由于 R+C 是搜索树的最大深度。

以上是关于如何确定这段代码的时间复杂度?的主要内容,如果未能解决你的问题,请参考以下文章

这段代码的时间和空间复杂度是多少?

这段代码的时间复杂度是多少(来自 leetcode)?

时间复杂度和空间复杂度

这段重复减去值的代码的复杂性是啥?

如何确定算法的内存和时间复杂度?

确定代码段的时间复杂度