我有一个简单的递归解决方案如下:
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
这棵树的最大深度是(x + y),每一步完成的工作是一个常数。因此,完成的最大工作是(x + y)* c,因此运行时复杂度为O(x + y)
问题1:我说错了吗?我相信我计算的上限不够紧
问题2:接下来,如果我要使用memoization来改善运行时间,因此不会重复计算子问题,那么Big-o所描述的运行时复杂度会如何变化?
答案 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)