递归:
- 在调用递归函数前,系统要保留以下三个信息:
- 返回地址
- 本函数调用时的实参值和函数值
- 被调用函数的局部变量值
- 尾递归
- 递归调用语句只有一个,而且处于算法的最后,例如求n!
- long Fact (int n)
- {
- if (n == 0) return 1;
- return n * Fact (n -1);
- }
- 递归调用语句只有一个,而且处于算法的最后,例如求n!
- 单向递归:
- 指递归函数中有一处以上的递归调用语句,且处于算法的最后,歌词递归调用语句的参数只和主调函数有关,和相互之间的参数无关。
- 例如,斐波那契数列:
- long Fib(int n)
- {
- if (n == 0 || n == 1) return n; // 递归出口
- else return Fib(n - 1) + Fib(n - 2); // 递归调用
- }
- 模拟系统的运行时栈消除递归
- 例如汉诺塔问题
- void Tower(int n, char fromPeg, char auxPeg, char toPeg)
- {
- if (n == 1)
- {
- cout << "Move Disk 1 from Peg " << fromPeg << "to Peg " << toPeg << endl;
- return;
- }
- Tower(n - 1, fromPeg, toPeg, auxPeg);
- cout << "Move Disk " << n << " from Peg " << fromPeg << "to Peg " << toPeg << endl;
- Tower(n - 1, auxPeg, fromPeg, toPeg);
- }
- 例如汉诺塔问题
递归转非递归:
- 尾递归转非递归:
- 由于每次递归调用时保存的返回地址,函数返回值和函数参数没有被使用。所以对于尾递归形式的算法可以转变为循环结构的算法,例如求n!
- long Fact2 (int n)
- {
- long fac = 1;
- for (int i = 1; i <= n; i++)
- fac = fac * i;
- return fac;
- }
- 由于每次递归调用时保存的返回地址,函数返回值和函数参数没有被使用。所以对于尾递归形式的算法可以转变为循环结构的算法,例如求n!
- 单向递归转非递归
- 单向递归主要找返回值存在的关系,比如斐波那契数列数列中,F(n-1)和F(n-2)就是一组相邻的数,所以用两个变量代替,其他特殊情况,可以用数组保持对应的序列值。
- long Fib2(int n)
- {
- if (n <= 1)
- return 0;
- long fib1 = 1, fib2 = 1;
- for (int i = 2; i <= n; i++)
- {
- fib2 = fib1 + fib2; // 得到新Fib2(n)的值
- fib1 = fib2 - fib1; // 得到新Fib2(n-1)的值
- }
- return fib2;
- }
- 单向递归主要找返回值存在的关系,比如斐波那契数列数列中,F(n-1)和F(n-2)就是一组相邻的数,所以用两个变量代替,其他特殊情况,可以用数组保持对应的序列值。
- 模拟系统的运行时栈转换递归为非递归
- 由于韩罗塔问题的递归算法中有两次递归调用,加上主调函数,因此非递归模拟中应该有三个模仿返回地址。翻个返回地址分别为返回主调函数,返回第一次递归调处和第二次递归调用出
- 机械式转换的方法:
- (1). 设置一工作栈作为当前工作记录
- 在函数中出现的所有参数和局部变量都必须用栈中相应的数据成员代替:
- 返回语句标号域 (t+2个数值)
- 函数参数(值参、引用型)
- 局部变量
- 在函数中出现的所有参数和局部变量都必须用栈中相应的数据成员代替:
- (2). 设置 t+2个语句标号
- label 0 :第一个可执行语句
- label i (1<=i<=t):第i个递归返回处
- label t+1 :设在函数体结束处
- (3). 增加非递归入口
- (4). 替换第 i (i = 1, …, t)个递归规则
- 假设函数体中第i (i=1, …, t)个
- 递归调用语句为:recf(a1, a2, …,am);
- 则用以下语句替换:
- S.push(i, a1, …, am); // 实参进栈
- goto label 0;
- ......
- label i: x = S.top();S.pop();
- /* 退栈,然后根据需要,将x中某些值赋给栈顶的工作记录S.top () — 相当于把引用型参数的值回传给局部变量 */
- 假设函数体中第i (i=1, …, t)个
- (5). 所有递归出口处增加语句:goto label t+1;
- (6). 标号为t+1的语句的格式
- (7). 改写循环和嵌套中的递归
- 对于循环中的递归,改写成等价的goto型循环
- 对于嵌套递归调用
- 例如,recf (… recf()),改为:
- exmp1 = recf ( );
- exmp2 = recf (exmp1);
- ...
- (8). 优化处理
- 去掉冗余进栈/出栈
- 根据流程图找出相应的循环结构,从而消去goto语句
- (1). 设置一工作栈作为当前工作记录
- 递归转非递归(模拟栈)代码举例:
- 优化后汉诺塔转换为非递归代码(后续)