让递归关系为T(n) = a * T(n/b) + O(n)。
这个递归意味着有一个递归函数:
- 将原问题分解为
a子问题
- 如果当前问题大小为
n,则每个子问题的大小将为n/b
- 当子问题很简单(太容易解决)时,不需要递归,直接解决(这个过程需要 O(n) 时间)。
当我们说原始问题被划分为a子问题时,我们的意思是在函数体中有a递归调用。
例如,如果函数是:
int f(int n)
{
if(n <= 1)
return n;
return f(n-1) + f(n-2);
}
我们说问题(大小为n)分为两个子问题,大小为n-1 和n-2。递归关系为T(n) = T(n-1) + T(n-2) + c。这是因为有 2 个递归调用,并且具有不同的参数。
但是,如果函数是这样的:
int f(int n)
{
if(n <= 2)
return n;
return n * f(n-1);
}
我们说问题(大小为n)仅分为1 个子问题,大小为n-1。这是因为只有 1 个递归调用。
所以,递归关系将是T(n) = T(n-1) + c。
如果我们将T(n-1) 与n 相乘,这看起来很正常,我们表示进行了n 递归调用。
请记住,我们形成递归关系的主要动机是对递归函数进行(渐近)复杂性分析。尽管 n 看起来不能从关系中丢弃,因为它取决于输入大小,但它的用途与在函数本身中的用途不同。
但是,如果您说的是函数返回的值,则应该是f(n) = n * f(n-1)。在这里,我们乘以n,因为它是一个实际值,将用于计算。
现在,来到T(n) = T(n-1) + c 中的c;它只是建议当我们解决大小为n 的问题时,我们需要解决大小为n-1 的较小问题以及其他一些常量(常量时间)工作,例如比较、乘法和返回值也会被执行。
即使使用递归树方法,我们也永远无法将“恒定工作量c”分成T(n/2) 和T(n/2) 两部分。事实上,我们正在将问题分成两半。在递归树的每一级中,每次递归调用都需要相同的“c”工作量。
如果有像T(n) = 2T(n/2) + O(n)这样的循环关系,其中要完成的工作量取决于输入大小,那么每个级别要完成的工作量将在下一个级别减半,就像你一样描述。
但是,如果递归关系类似于T(n) = T(n-1) + O(n),我们不会在下一个递归级别将工作量分成两半。我们只会在每个连续级别将工作量减少一个(n 大小的问题在下一个级别变成 n-1)。
要检查工作量将如何随着递归而变化,请将替换方法应用于您的递归关系。
希望我已经回答了你的问题。