递归算法转换为非递归算法
递归算法实际上是一种分而治之的方法,它把复杂问题分解为简单问题来求解.
对于复杂问题,递归算法是一种自然且合乎逻辑的解决问题的方式,但是递归算法的执行效率通常比较差.
因此,在求解某些问题时,常采用递归算法来分析问题,用非递归算法来求解.
将递归算法转化为非递归算法有两种方法:
方法一, 直接转化法 (直接求值,不需要回溯).
方法二, 间接转化法(不能直接求值,需要回溯).
1. 直接转化法: 通常用来消除尾递归和单向递归,将递归结构用循环结构代替
(1)尾递归是指在递归算法中,递归调用语句只有一个,而且是处在算法的最后
例如:
阶乘的递归算法:
long fact (int n) { if(n == 0) return 1; else return n*fact(n-1); }
分析: 当递归调用返回时,是返回到上一层递归调用的下一条语句,而这个返回位置正好是算法的结束处,
所以不需要栈来保存返回信息.
上述尾递归例题可以用循环结构来代替,如下:
long fact(int n) { int s = 1; for(int i=1;i<=n;i++) s = s + i; return s; }
(2) 单向递归是指递归算法中虽然有多处递归调用语句,但各递归调用语句的参数之间没有关系,
并且这些递归语句都处在递归算法的最后.显然尾递归是单项递归的特例.
例如:
求斐波那契数列的递归算法:
int f(int n) { if(n==0) return 0; if(n==1) return 1; return f(n-1)+f(n-1); }
对于单向递归,可以设置一些变量保存中间结果,将递归结构用循环结构代替.
上述单向递归的斐波那契数列用循环实现,如下:
int f(int n) { int i,s; int s1=1,s2 = 0; for(i=1;i<n;i++) { s = s1+s2; s2= s1; //用来保存f(n-2)的值 s1=s; //用来保存f(n-1)的值 } return f(n-1)+f(n-1); }
2. 简介转化法:
该方法使用栈保存中间结果,一般需要根据递归函数在执行过程中栈的变换得到.
其一般过程如下:
将初始状态s0入栈; while(栈不为空) { 将栈顶元素赋值给s,出栈; if(s是要找的结果) 返回; else{ 继续寻找; 将s1入栈; } }
这是数据结构C++版教材中的介绍的.
我感觉还是具体情况具体分析,没有固定的套路,只要把问题分析清楚了,代码也就不难写了
总结一下:
其递归过程是:
①先将n个问题规模按照一定步长分解为n-1个问题规模,再分解为n-2个问题规模,依次执行这个过程,最后将问题规模分解为1.
即先让问题规模n入栈,再让n-1个问题规模入栈,依次入栈,最后栈顶为问题规模1.
②从问题规模1开始计算并返回,再计算问题规模2并返回,依次执行,直到问题规模n计算完毕并返回
此过程即为出栈过程.
因此递归的过程就是将大问题分解为小问题,从小问题着手各个击破,因此也验证了分而治之的思想
3. 递归规过程有无返回值
上述的阶乘和斐波那契数列都是有返回值的函数
下面来讨论一下无返回的情况
阶乘无返回值:
void fact (int n , int &sum) //sum初始化为1 { if (n == 0) { return; } else { //sum = sum*n; //sum放在递归之前,即在入栈的过程中将问题解决 fact(n - 1, sum); sum = sum * n; //sum放在递归之后,即在出栈的过程中将问题解决 } }
斐波那契数列无返回值:
//无返回值,斐波那契数列求值 void Fibonaci(unsigned int n, unsigned int &sum) { if (n == 0) { return ; } if (n == 1) { sum = sum+1; return; } unsigned int a = 0; unsigned int b = 0; Fibonaci(n - 1, a); Fibonaci(n - 2, b); sum = a + b; }
总结: 上述的递归返回值使用函数参数来代替.
由此可以看出使用递归求值时,有返回值的递归函数比无返回值的函数要更简洁且更易理解.
当使用递归的过程解决问题时,就没必要使用返回值.
递归函数有无返回值哪个更合适,这需要具体情况具体分析了.
这也是设计一般函数选择有无返回值的判断依据:
①如果需要计算完成时的结果只有一个,则可以用返回值直接返回.
②如果返回结果是多个,则需要函数参数辅助返回(即,使用参数的指针或引用传递).