昨天刚写完今天又来写了,因为怕自己这一块的知识掌握的不太牢固,所以其实这篇文章我是写给自己看的嘻嘻。
在C语言中,递归其实应该拆开,递=递推,归=回归,而递归就是函数自己调用自己。


比如这个,史上最简单的函数递归。不过运行的时候会出现"stack overflow"的报错,因为你在main函数内部再次调用main函数,不断递归下去就造成了栈溢出的问题,因为每调用一次函数都需要在栈区申请一块内存空间。
递归的思想:把一个大型复杂的问题,转换为与原问题相似,但规模较小的子问题,就是大事化小的过程。
举例1:求n的阶乘
n!=n*(n-1)*(n-2)....*2*1,这是大家熟知的阶乘公式,但是其实还有另一种拆分方法
n!=n*(n-1)! (n-1)!=(n-1)*(n-2)!.....以此类推,就可以实现“大事化小”的想法,而用函数表示的话如下:假定FAC(n)=n! ,FAC(n)=n*FAC(n-1), FAC(n-1)=(n-1)*FAC(n-2).....这就是递推的具体函数实现,还是比较好理解的,所以接下来我用代码实现一下。

这张图中,由于0!为1,所以当形参n为0时返回1,其余时候就是实现函数递归的过程,为让过程更加直观,这里还是采用监视的方式。

这里我输入n=4,开始逐步调试,按下F11进入函数内部,递归一次过后

在监视图中可以看到n变为3,这就是进入了递归的过程,再次递归

函数值变为2,以此类推,函数值会在变为0后返回1,接下来,就是回归的过程

回归开始的时候,n=0,这个时候就会return 1,然后,n=1,return n*FAC(n-1,也就是return n*FAC(0),而FAC(0)为1,也就是return 1,然后,n=2,return n*FAC(1),也就是2*1,然后n=3,n=4,以此类推,整个的计算过程就是1*2*3*4,也就实现了计算阶乘。
但是递归需要有限制条件,比如上面这个代码中,限定了如果n==0时返回1,就由递推过程到了回归过程,而且在递推过程中,n的值在不断减小,也就是在不断接近这个限定条件,所以这二者缺一不可。1.递归需要有限制条件。2.递归时逐渐接近这个限制条件。
举例2:顺序打印一个整数的每一位。
这里我就直接上代码了

同样在print函数内部调用,也就是函数递归的方式,限制条件为n>9,例如我输入1234,print(1234/10),即123,继续print(123/10)=12,最后变为1,时,限定条件不成立,跳出if语句,打印n%10的值,也就是1,然后回归12,n%10为2,以此类推,打印出1 2 3 4.
这样看来,递归好像非常方便,是不是可以经常使用(随便乱用)呢?当然不是了。就像前面讲到的,每次函数调用都要申请栈帧空间,递推的时候申请,而回归的时候才会逐层释放栈帧空间如果递归层次太深

还是这个阶乘的代码,计算30的阶乘还是可以的

但是再稍微增大n的值,比如40,就无法计算,这就是因为递归层次太深造成的,浪费了太多栈帧空间。那有没有什么改进办法呢?有的兄弟有的,这个时候就要采用迭代(循环)的方式改进了。

上面这段代码就是一个利用迭代实现阶乘计算的例子,而且效率比递归更高。
举例3:求第n个斐波那契数。

同样是利用递归,因为每个斐波那契数都等于前两个斐波那契数之和,所以采用递归思想解决,但是思考一下稍微极端一点的情况,就是比如要求第40,50个斐波那契数

这里我要计算第50个斐波那契数,程序已经无法计算了,这是因为在递归的过程中存在大量的重复运算。

输入50,递推结束,回归的时候从50开始往回推,由上图可见,有大量的重复运算。为了更直观,比如我想知道第三个斐波那契数被使用了多少次

这里我定义一个全局变量i,每当n==3时就++,可以看到计算结果i居然计算了三千九百多万次,这还只是n==3的情况,因此存在大量重复运算,也是递归的一个弊端。那么迭代要怎么实现呢?

这张图中,因为斐波那契数列最开始两个值为1,初始化a,b为1,然后经过一系列赋值操作,使c,也就是第n个斐波那契数始终为前两个数之和,而这个n--呢,是因为比如说,第三个斐波那契数,需要循环一次,第四个循环两次,第五个循环3次...差值始终为2,所以只要在n>2时进入循环就可以实现斐波那契数的计算。

这个时候即使输入的数值很大,计算结果即使不对,但是最起码输出的速度快了很多。迭代的效率确实比递归高很多。或者再简化一下这个代码:

将c初始化的值改为1,如果n<2,不进入循环直接返回1.
好了就写到这吧,青蛙跳台阶的问题还没解决....
1114

被折叠的 条评论
为什么被折叠?



