递归解决方案的大O复杂性

时间:2015-02-28 14:18:36

标签: java algorithm recursion runtime big-o

我有一个简单的递归解决方案如下:

public int countPaths(int x, int y) {

    if(x == 0 && y == 0) {
        return 0;
    } else if(x == 0) {
        return 1;
    } else if(y == 0) {
        return 1;
    } else {
        int count = countPaths(x-1, y);
        count += countPaths(x, y-1);
        return count;
    }
}

这是为了解决书中的以下问题:Cracking the coding interview

想象一下,机器人坐在X by Y网格的左上角。机器人只能向两个方向移动:向右和向下。机器人从(0,0)到(X,Y)的路径有多少?

我试图确定运行时间的复杂性,我相信它是O(x + y)。我通过使用递归树来达到此目的,例如,如果x = 2且y = 2 enter image description here

这棵树的最大深度是(x + y),每一步完成的工作是一个常数。因此,完成的最大工作是(x + y)* c,因此运行时复杂度为O(x + y)

问题1:我说错了吗?我相信我计算的上限不够紧

问题2:接下来,如果我要使用memoization来改善运行时间,因此不会重复计算子问题,那么Big-o所描述的运行时复杂度会如何变化?

3 个答案:

答案 0 :(得分:2)

虽然树的深度确实是O(x + y),但每层的节点越来越多,而且它是决定复杂性的节点数,而不是树的深度。

如果你写下运行时的递归关系,你会得到:

T(0, y) = T(x, 0) = 1
T(x, y) = T(x-1, y) + T(x, y-1) + 1

如果你忽略第二个等式上的+1(只能使运行时间更好),你得到的代码首先是你的代码计算的功能,即选择(x + y,y)

对于x = y,这是中心二项式系数,大约为4 ^ x / sqrt(pi * x),即使是中等大小的x也足以使算法无效。

通过记忆,你为x和y的每个值做了一定量的工作,所以复杂度是O(xy)。

答案 1 :(得分:0)

如果您根据评估给定对(x, y)的计数所需的添加次数来评估复杂性,则会出现重现

A(x,y) = A(x-1,y) + A(x,y-1) + 1

A(x,0) = A(0,y) = 0

设置A(x,y) = P(x,y) - 1重复发生

P(x,y) - 1 = P(x-1,y) - 1 + P(x,y-1) - 1 + 1,

P(x,y) = P(x-1,y) + P(x,y-1),

使用P(x,0) = P(0,y) = 1,它给出了经典的Pascal三角形和

A(x,y) = (x+y)!/(x!y!) - 1.

您还可以使用递归函数调用的次数

C(x,y) = C(x-1,y) + C(x,y-1) + 2,

C(0,y) = C(x,0) = 0

您可以通过设置C(x,y) = 2P(x,y) - 2并获取

来解决此问题
C(x,y)= 2(x+y)!/(x!y!)-2.

关于渐近复杂性,这没有区别。公式并不比O((x+y)!/x!y!)更简单。

通过记忆,每次评估(x, y>0)只需要一次或两次调用,并且假设存储/检索值的时间恒定,总复杂度要好得多O(xy)

答案 2 :(得分:0)

基于来自@Anonymous的有价值的输入,我们知道递归关系是:

T(x, y) = T(x-1, y) + T(x, y-1) + 1
Abusing (which is ok in Big-O analysis) the notation, let x = y 
T(x, x) = 2 * T(x-1, x) = 2 * 2 * T(x-2, x) = ... = 2 * ... * 2 * T(0, x)
        = O(2^x)

因此运行时复杂度为

  

O(2 ^ n);其中n = max(x,y)

通过记忆,我明白了,谢谢@Anonymous,它应该是 O(xy)