再回递归调用
来源:互联网 发布:sql数据库软件 编辑:程序博客网 时间:2024/05/21 22:33
<pre name="code" class="cpp">递归调用是困扰我很久的问题,作为一名“新手”,我一直不知如何理解递归调用,即便是看了书上写的“当函数直接或间接地调用自己时,便发生了递归”。恰好今天重新看了书,还有看了一篇博客,令我大受启发。(博客地址:http://blog.csdn.net/vagrxie/article/details/8470798
1.首先来记录一下书上的解释。
如果递归函数调用自己,则被调用的函数也将调用自己,这将无线循环下去,除非代码中包含终止调用链的内容(语句)。 通常的方法将递归调用放在if语句中。例如,void类型的递归函数recurs()代码如下:
void recurs(arguments){ statements1; if (test) recurs(arguments); statements2;}test最终将为false,调用链将断开。
只要if语句为true,每个recurs()调用都将执行statement1,然后再调用recurs(),而不会执行statement2。当if语句为false时,当前调用将执行statement2。当前调用结束后,程序控制权将返回给调用它的recurs(),而该recurs()将执行其statement2部分,然后结束,并将控制权返回给前一个调用,以此类推。因此,如果recurs()进行了5次递归调用,则第一个statement1部分将按函数调用的顺序执行5次,然后statement2部分将以与函数调用相反的顺序执行5次。进入了5层递归后,程序将沿进入的路径返回。
2.再看看那篇博客的解释。
例题:用递归调用来求斐波那契数列
程序:
#include <iostream>using namespace std;int main(){int f(int);cout<<f(5)<<endl;return 0;}int f(int n){if (n==1||n==2) return 1;else return f(n-1)+f(n-2);}分析:
n=5
f(5)=f(4)+f(3)
f(4)=f(3)+f(2)
f(3)=f(2)+f(1)
而f(2)=f(1)=1,所以
f(3)=1+1=2
f(4)=2+1=3
f(5)=3+2=5
我们怎么判断这个阶乘的递归计算是否是正确的呢? 先别说测试, 我说我们读代码的时候怎么判断呢?
回溯的思考方式是这么验证的, 比如当n = 4时, 那么factoria(4)
等于4 * factoria(3)
, 而factoria(3)
等于3 * factoria(2)
, factoria(2)
等于2 * factoria(1)
, 等于2 * 1
, 所以factoria(4)
等于4 * 3 * 2 * 1
. 这个结果正好等于阶乘4的迭代定义.
用回溯的方式思考虽然可以验证当n = 某个较小数值是否正确, 但是其实无益于理解.
Paul Graham提到一种方法, 给我很大启发, 该方法如下:
- 当n=0, 1的时候, 结果正确.
- 假设函数对于n是正确的, 函数对n+1结果也正确.
如果这两点是成立的,我们知道这个函数对于所有可能的n都是正确的。
这种方法很像数学归纳法, 也是递归正确的思考方式, 事实上, 阶乘的递归表达方式就是1!=1,n!=(n-1)!×n
(见wiki). 当程序实现符合算法描述的时候, 程序自然对了, 假如还不对, 那是算法本身错了…… 相对来说, n,n+1的情况为通用情况, 虽然比较复杂, 但是还能理解, 最重要的, 也是最容易被新手忽略的问题在于第1点, 也就是基本用例(base case)要对. 比如, 上例中, 我们去掉if n <= 1
的判断后, 代码会进入死循环, 永远不会结束.
上面讲了怎么理解递归是正确的, 同时可以看到在有递归算法描述后, 其实程序很容易写, 那么最关键的问题就是, 我们怎么找到一个问题的递归算法呢?
Paul Graham提到, 你只需要做两件事情:
- 你必须要示范如何解决问题的一般情况, 通过将问题切分成有限小并更小的子问题.
- 你必须要示范如何通过有限的步骤, 来解决最小的问题(基本用例).
如果这两件事完成了, 那问题就解决了. 因为递归每次都将问题变得更小, 而一个有限的问题终究会被解决的, 而最小的问题仅需几个有限的步骤就能解决.
这个过程还是数学归纳法的方法, 只不过和上面提到的一个是验证, 一个是证明.
- 再回递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- 递归调用
- iOS 打电话、发短信、发邮件
- lighttpd配置(转载)
- 34岁,重新开始!
- 解决浏览器缓存问题
- IName对象的理解
- 再回递归调用
- 修改查看MySQL编码格式【转载】
- 随机的力量(2) - 矩阵比较
- CentOS7配置Apache多站点VirtualHost
- Android BroadcastReceiver
- Java多线程(十)之ReentrantReadWriteLock深入分析
- 记录——《C Primer Plus (第五版)》第十章编程练习第八题
- 设计模式 一一一 装饰模式
- 乘法逆元