尾递归及其消除
先来看看递归倒置数组算法
// 调用形式 reverse(a, 0, n - 1)
void reverse(int* p, const int& l, const int& r) {
if (l < r) {
std::swap(p[l], p[r]); // 交换
reverse(p, l + 1, r - 1); // 递归倒置p(l, r)
} // 隐含了两种递归基
}
由于上述算法属于尾递归,所以我们可以用迭代法对其进行消除
// 调用形式 reverse(a, 0, n - 1)
void reverse(int* p, int l, int r) {
while (l < r) { // 用 while 替换跳转标志和 if,完全等效
std::swap(p[l++], p[r--]); // 交换
}
}
☆ 只有当当该算法( 除平凡递归基外 )任一实例都终止于这一递归调用时,才属于尾递归。
Fibonacci
- 递归实现版
const int fib(const int& n) {
return 2 > n ? n : fib(n - 1) + fib(n - 2);
}
下面是改进版,基于动态规划策略计算 Fibonacii 数
int fib(int n) { // 迭代版
int f = 1, g = 0; // 初始化 fib(-1),fib(0)
while (0 < n--) {
g += f;
f = g - f; // 依据原始定义,通过 n 次加法和减法计算 fib(n)
}
return g;
}
- 这里仅使用了两个中间变量 f 和 g,记录当前的一对相邻 Fibonacci 数。整个算法仅需线性迭代,时间复杂度为 σ(n)。更重要的是,该版本仅需常数规模的附加空间,空间效率也有极大提高。