这两种方法都是正确的。从一个函数中进行多次递归调用确实是合法的,其含义就是您所想的 - 只需进行一次调用,然后进行下一次调用,然后再进行下一次调用,等等。
有趣的是,我认为递归版本的调用次数不会成倍增加。它最多进行两次递归调用,但每个都在一个问题上,其大小(大约)是原始调用的一半。本质上,重现看起来像这样:
T(1) = 1
T(2) = 1
T(n) <= T(n / 2) + T(n / 2 + 1) + 1
我用“小于或等于这里”来表示,在最好的情况下你可能只打一个电话,但在最坏的情况下你最多打两个电话。
我想证明这个函数 T(n)
T(1) = 1
T(2) = 1
所以让我们设置 a = 1。对于归纳步骤,我们考虑三种情况。首先,让我们考虑何时 floor(n / 2)
T(n) <= T(n / 2) + T(n / 2 + 1) + 1
<= 1 + 1 + 1
<= 3
如果我们假设 cn + d >= 3,当 n = 3 或 n = 4 时,那么这个结果是正确的。特别是,这意味着 3c + d >= 3 和 4c + d >= 3。
在下一种情况下,让我们看看如果 floor(n / 2) = 2 会发生什么。然后我们就有了
T(n) <= T(n / 2) + T(n / 2 + 1) + 1
<= 1 + max{c(n / 2 + 1) + d, 1} + 1
<= 2 + max{c(n / 2 + 1) + d, 1}
<= 3 + c(n / 2 + 1) + d
所以如果我们有 3 + c(n / 2 + 1) + d
3 + c(n / 2 + 1) + d <= cn + d
3 + c(n / 2 + 1) <= cn
3 + c(5 / 2 + 1) <= 5c
3 + 5c/2 + c <= 5c
3 + 7c/2 <= 5c
4 <= 3c / 2
8 / 3 <= c
所以我们必须有 c >= 8 / 3。
最后,n / 2 和 n / 2 + 1 都不小于三的情况:
T(n) <= T(n / 2) + T(n / 2 + 1) + 1
<= c(n / 2) + d + c(n / 2 + 1) + d + 1
<= cn / 2 + cn / 2 + c + 2d + 1
= cn + c + 2d + 1
如果小于 cn + d
cn + c + 2d + 1 <= cn + d
c + 2d + 1 <= d
c + d + 1 <= 0
这在 d = -c - 1 时有效。
从前面开始,我们知道 3c + d >= 3,如果 2c - 1 >= 3 或 2c >= 4,则有效,因此 c >= 2。我们也有 4c + d >= 3,它如果 c >= 2 也有效。让 c = 8 / 3,我们得到 d = -11 / 3,所以
T(n) <= max{8n/3 - 11/3, 1}
所以 T(n) = O(n) 并且递归只会进行线性多次调用。
简而言之,递归和迭代版本都需要线性时间。不要害怕递归的指数时间爆炸,而不能确定它是指数的。 :-) 诚然,在这种情况下,我真的更喜欢迭代版本。它更清晰、更直观、更直接 O(n)。