上篇文章介绍了一下函数的基本内容,这次来讲讲函数的编程技巧--递归,迭代。并运用这两个技巧实现的斐波那契数列。
递 归
递归是一个过程或函数在其定义或说明中直接或间接调用自身的一种方法,把一个复杂的问题层层转化为与原问题相似的小问题求解。
特点是只需要少量的程序就可以描述出解题过程中需要的重复计算,大大减少了计算量。(大事化小)
下面讲一个小例子具体介绍一下递归,然后再实现斐波那契数列。
练习:接收一个无符号的整型,按顺序输出它的每一位。例:输入1234,打印1 2 3 4。
写这个程序之前,我们先思考一下该如何才能实现这个功能。
在用户输入整数,这里举例输入1234,如何分解为一个个的数字,首先我想到的就是除10,由于int 只接收整形,所以每除一个10,就可以少一位。这样就可以实现分解为一个个数据了。那么问题又来了,我除三个10可以获得1,除两个10的时候获得的是12,这时候要打印出去的话就是12了,如何打印2出来呢,这里我想到的是用求模,跟10求模。为什么选择的是10,不是1,不是100,因为这里要求的是打印一位数,求10才能实现这个功能。第一个1跟10求模还是1,12跟10求模为2,123跟10求模是3,可以看出完美实现了题目的要求。现在就要想想如何实现这个过程了。
![](https://i-blog.csdnimg.cn/blog_migrate/b473bea240a094d4453e87c54bbf233b.png)
可以看到这个解题的过程很符合我们递归的特点,大事化小。那我们就用递归来实现一下。
![](https://i-blog.csdnimg.cn/blog_migrate/65a091fa1d70df71f239bf56c31cef5c.png)
可以看到完美符合题目要求,再仔细看下代码,发现我们的Print函数中又包含了Print函数,这其实就是我们递归的实现了。那么它的实现过程又是怎么样的,是如何‘大事化小’的。
![](https://i-blog.csdnimg.cn/blog_migrate/3cb8222e140b227b163f9e5fd4ac1c60.png)
这里分成两大步份讲解。下面把四个步骤中的Print函数,分别命名为Print1,Print2,Print3,Print4。
递推:
第一步我们输入1234输入Print1函数里,通过if语句判断大于9进入语句,里面又是Print函数,但传入的参数变成n\10。所以传入Print2函数的n变成了123。
第二步Print2接收到123还是大于9,进入Print3函数,变成了12。
第三步Print3接收到12还是大于9,进入Print4函数,变成了1。
第四步Print4接收到1,要小于9,不进入if语句,直接到了printf语句,打印1跟10求模的值,1。
回归:
第五步因为我们是一步步下来的,之前的Print1,2,3的函数并没有进行完,所以在第四步的Print函数打印完1之后。我们会回到Print3函数,这个时候已经把if语句走完了,到了printf语句,又因为这个时候n是等于12的,所以n%10是等于2的。依此类推,回到Print2就打印3。Print1就打印4。
程序结束。
上面的过程可以看到,递归其实是分为两个步骤进行的,首先是递推,把大问题化成一个个小问题。接着就是回归,把问题一个个解决。
那么什么样的才算是一个合格的递归呢。递归必须满足俩个条件。
存在限制条件,当满足这个限制条件的时候,递归结束。
每次的递归调用都应该更加接近限制条件。
ok,已经把递归基本内容说的差不多了。让我们进入今天的正题:斐波那契数列。
简单介绍一下斐波那契数列。
斐波那契数列(Fibonacci sequence), 斐波那契数列指的是这样一个数列: 1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584......
观察斐波那契数列数列可以看到除了前两个数之外,它的每一个数都等于它的前两个数相加。举例第5个数:5,它等于3加2,而三又等于它前面两个数1加2,2又等于它的前两个数相加1和1。这样的过程就是递归,一直不断的划分下来其实到最后就看有几个1相加,让我来实现一下。
![](https://i-blog.csdnimg.cn/blog_migrate/da6688d8357021941536a80a539196eb.png)
可以看到这里我的限制条件就是n<=2的时候结束递归,因为我发现的规律是从第三个数开始的,所以递归只能从第三个数开始。满足第一个条件。在看看else,我每次返回的就是这个数的前两个值相加。只要返回的这两个值不大于2,就一直返回。第一步用了两次这个函数,然后分解的来个函数再次分解成四个函数,然后再分解为八个函数......可以发现这不是这次数不是2的n次方吗。那么可想而知,涉及到次方的话,这个数字一但大起来,就会非常的大,你程序运行的就非常慢。我尝试了一下,真的半天没有反应。
![](https://i-blog.csdnimg.cn/blog_migrate/63efd0206523cc5c04b5871f262713da.png)
可以看一下这个过程,重复的数字有多少,这样的效率太低了。如果还不够直观的话,我再写一个函数给你们看看。
![](https://i-blog.csdnimg.cn/blog_migrate/71b82ca08935efc450fed678fd851f12.png)
这里的意思是2被统计过多少次。可以看到一个非常大的数字。如果数字再大的话,可以就会造成栈溢出了。所以这个递归虽然可以实现这个过程,但不是很好,这个时候就尽量不要用递归了。可以考虑使用迭代的方法去实现这个过程。
迭 代
![](https://i-blog.csdnimg.cn/blog_migrate/bb0d3b5ce0d522eba26b8eb630ec1abd.png)
这里我实践了一下,同样是数字40,迭代会比递归快很多。你们有兴趣也可以实践一下,不想手打的话。可以找我要发源码。
这里迭代的实现过程是这样的我把斐波那契数列前两个数直接设置为a,b。作为整个函数的基础。斐波那契数列中1,1同样是基础。再设置一个值作为‘第三个数’。直接把它设置为1,因为如果你输入1或者2的时候,我就直接返回1了。设置成其他的话,就会出错。这时候实现过程是这样的:
首先输入的数字大于等于3,不管它多少次。进入到循环之后,我从斐波那契数列的第三个数开始算起,等于1+1,然后把第二个数的值b=1赋给第一个数a。再把算出来的第三个数的值c=2赋给b。这时候第一个和第二个值就发成变化,各自前移了一步。每进行完一次循环之后就n--。n就是代表了你要前移的步数,也就是你想求的值的位置。按照这个步骤一直循环,一直前移。比起递归大大增加了效率。
![](https://i-blog.csdnimg.cn/blog_migrate/686f58853db447b8eb9085d37ddd7383.png)
这就是我有关递归和迭代的理解,如果哪里讲错了,欢迎指出,我会进行改正。谢谢。