最近学习到了递归,刚开始看,真是头大,函数里面嵌套其本身,到底是怎么个流程啊?
现在,咱们先了解下递归函数的数学原理:
高中的时候就出现很多递归函数,应该是在“级数”那里的习题中出现的,而且还不少。还是从例子开始吧:
f(x)=f(x-1)+x*x ,其中x>0且f(0)=0求f(4)
解: 由于f(0)=0:
当x=1 时 f(1)=f(0)+1*1=1;
当x=2 时 f(2)=f(1)+2*2=5;
当x=3 时 f(3)=f(2)+3*3=14;
当x=4 时 f(4)=f(3)+4*4=30;
所以, f(4)=30.
上学的时候,可能会这样做出来。
f(x)=f(x-1)+x*x ,其中x>0且f(0)=0就是一个递归函数,它用到了f(x)是用f(x-1)定义的。细心的人还可以发现x>0且f(0)=0也是函数的一部分:
x>0提供一个递归区间,而f(0)=0提供了一个初始条件(思维方向不同,在电脑思维中这个条件为终止条件,详见下文)。
或许大家觉得和我们课堂上的递归还是有点不同,不同在哪呢?
这就是人脑和电脑的区别:
电脑不会直接去找初始条件去向问题递推。
而是从问题出发,递推下去,直到找到终止条件(解题时的初始条件)。
电脑思维:
f(4)=f(3)+4*4;
f(3)=f(2)+3*3
f(2)=f(1)+2*2
f(1)=f(0)+1*1
f(0)=0; //终止条件
f(1)=f(0)+1*1=1;
f(2)=f(1)+2*2=5;
f(3)=f(2)+3*3=14;
f(4)=f(3)+4*4=30;
这个是电脑的思维过程,也就是计算过程,不会在前台显示出来。
“遇到问题,解决问题,输出结果”——这是电脑处理问题的流程。
关键在于,怎么写个递归函数让电脑认识。
明白递归函数的定义,其实很简单。
递归函数有三个充分条件:第一是函数体,第二是递归区间,第三个是终止条件,
只要在代码中全部申明出来,一个递归函数的就写出来了。
上面的递归函数的就可以写出下面的代码:
- function squaresum($x){
- if($x>0) //递归区间
- $result=squaresum($x-1)+$x*$x; //函数体
- elseif($x=0) //终止条件
- return $result=0;
- return $result;
- }
- echo squaresum(4); //输出30
其中用到了if...elseif…语句,这就是来声明递归函数的递归区间和终止条件(x>0且f(0)=0)的。
现在在来写一个正整数n的n!的递归函数就思路很明确了。
分析:正整数n , f(n)=n! =>
函数体:f(n)=n*f(n-1); 递归区间:n.> 1; 终止条件:n=1;
- function rank($n)
- {
- if($n>1)
- $result=$n*rank($n-1);
- elseif($n=1)
- return $result=1;
- return $result.'<br>';
- }
由此我们可以发现当要写一个递归函数,找到终止条件,一个递归函数就很明朗了,剩下就是语法问题了
到linux C这块,我们做一个例题:
例:求斐波那契数列第n项。斐波那契数列的第一项和第二项是1,后面每一项是前两项之和,即1,1,2,3,5,8,13,。。。
下面程序采用直接递归调用:
- #include <stdio.h>
- long fib(int n)
- {
- if(n == 0 || n == 1)
- return 1;
- else
- return (fib(n-1)+fib(n-2));
- }
- int main()
- {
- int i;
- for(i = 0;i < 8;i++)
- printf("%ld ",fib(i));
- printf("\n");
- return 0;
- }
程序执行结果如下:
- fs@ubuntu:~/qiang/digui$ ./digui1
- 1 1 2 3 5 8 13 21
递归的条件:
上面已经简单提到,现在再说明一下
一个问题能否用递归来实现,看其是否有如下特点:
1、须有完成函数任务的语句。
例如:下面的代码定义了一个递归函数
- #include <stdio.h>
- void count(int val)
- {
- if (val > 1)
- count(val - 1);
- printf("OK:%d\n",val);
- }
该函数的任务是在输出设备上显示”ok: 整数值“。
2、一个任务是否能够避免递归调用的测试。
例如,上面的代码中,语句"if (val > 1)"便是一个测试,如果不满足条件,就不进行递归调用。
3、一个递归调用语句
该递归调用语句的参数应该逐渐逼近不满足条件,以至最后断绝递归。
例如,上面的代码汇总,语句 "if( val > 1)"便是一个递归调用,参数在渐渐变小,这话总发展趋势能使测试 "if (val > 1)"最终不满足。
4,、先测试,后递归调用
在递归函数定义中,必须先测试,后递归调用。也就是说,递归调用是有条件的,满足了条件,才可以递归。
例如,下面的代码无限制的调用函数自己,造成无限制递归,终将使栈空间溢出;
- #include <stdio.h>
- void count(int val)
- {
- count(val - 1);//无限制递归
- if (val > 1)
- printf("OK:%d\n",val);
- }
下面是完整程序:
- #include <stdio.h>
- void count(int val)
- {
- if (val > 1)
- count(val - 1);
- printf("OK:%d\n",val);
- }
- int main()
- {
- int n = 10;
- count(n);
- return 0;
- }
程序执行结果如下:
- fs@ubuntu:~/qiang/digui$ vi digui2.c
- fs@ubuntu:~/qiang/digui$ gcc -o digui2 digui2.c
- fs@ubuntu:~/qiang/digui$ ./digui2
- OK:1
- OK:2
- OK:3
- OK:4
- OK:5
- OK:6
- OK:7
- OK:8
- OK:9
- OK:10
- fs@ubuntu:~/qiang/digui$
递归的应用会继续更新,比如在二叉树的遍历