目录
1.什么是递归调用?
在调用一个函数出现直接或间接地调用该函数本身,称为函数的递归调用
直接调用
void f() { ... ... f();//调用语句 ... }
间接调用
(下面这个代码中f函数调用了g函数,g函数又调用了f函数)
void f() { ... ... g();//调用g函数 ... } void g() { ... ... f();//调用f函数 ... }
从以上两个函数可以看到,这两种递归调用都是无终止的自身调用。显然,程序中不应该出现这种无终止的递归调用,而只应该出现有限次数的、有终止的递归调用,这可以用if语句来控制,只有在某一条件成立时才继续执行递归调用;否则就不在继续。
2.什么是递归?
递归中的递就是递推,归就是回归
3.递归举例
3.1求n!的阶乘
3.1.1.非递归法
在进入递归之前,先体会一下非递归法
#include<stdio.h> int fac (int n) //n为形参 { int i,m=1; for(i=1;i<=n;i++) { m=m*i; //用循环累乘:m=1*2*...*n } return m; } int main() { int n,y; scanf("%d",&n);//输入n y=fac(n);//n为实参 printf("%d!=%d",n,y); return 0; }
(注:实参和形参可以重名,如果主程序中输入n值是4,调用函数时,则是把其值4传递给函数的形式参数n,形参n的值即为4,实参仅是做的一个值的传递,这个过程与实参的名字是什么无关,所以形参和形参名字相同不影响传值调用)
3.1.2.递归法
3.1.2.1分析和代码实现
n!=n*(n-1)!
4!=4*3*2*1
3!=3*2*1
所以:4!=4*3!
当n==0的时候,阶乘为1,其余用公式计算
先将整体代码列给大家
#include"stdio.h" int fact(int n) { if(n==0) return 1; else return n*fact(n-1); } int main() { int n=0; scanf("%d",&n); int r=fact(n); printf("%d!=%d",n,r); return 0; }
下面用n=5时的例子给大家解释
fact(5)虽然=5*fact(4),但是fact(4)又是一个调用函数,再次调用fact函数,然后求得fact(4)为多少后,别忘记乘上前面的5,也可按照上图一步一步算
因为你调用完得出的值得返回去
递归是用少量的代码完成大量复杂的运算
3.2顺序打印一个整数的每一位
分析:输入一个整数,按照顺序打印一个整数的每一位
eg:
输入:1234 输出:1 2 3 4
3.2.1分析和代码实现
1234
1234%10=4
1234/10=123
123%10=3
123/10=12
12%10=2
12/10=1
1%10=1
1/10=0
以上的方法很容易理解,但是过于复杂,所以下面我们用递归的方法解决此问题
仔细分析上图解析,可以的得出此代码为
#include<stdio.h> void print(int n) { if (n > 9) print(n / 10); printf("%d ", n % 10); } int main() { int n = 0; scanf_s("%d", &n); print(n); return 0; }
4.递归与迭代
fact函数是可以产生正确的结果,但是在递归函数中涉及一些运行时的开销。
在c语言中每一次函数调用,都需要为本次函数调用在内存的栈区申请一块内存空间来保存函数调用期间的各种局部变量的值,这块空间被称为运行时堆栈,或者函数栈帧。
函数不返回,函数对应的栈帧空间就一直占用,所以函数调用中存在递归调用的话,每一次递归调用都会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归,开始回归,才逐层释放栈帧空间。
所以如果采用函数递归的方式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出的问题。
所以我们可以使用迭代的方法(循环)
#include<stdio.h> int fact(int n) { int i = 0; int ret = 1; for (i = 1; i <= n; i++) { ret *= i; } return ret; } int main() { int n = 0; scanf_s("%d", &n); int r = fact(n); printf("%d", r); return 0; }
注:当一个问题非常复杂,难以用迭代的方法实现时,此时递归实现的简洁性便可以补偿它所带来的开销。
4.1举例:斐波那契数列
4.1.1迭代法
#include<stdio.h> int fib(int n) { int a = 1; int b = 1; int c = 1; while (n > 2) { c = a + b; a = b; b = c; n--; } return c; } int main() { int n = 0; scanf_s("%d", &n); int r=fib(n); printf("%d\n", r); return 0; }
4.1.2递归法
#include<stdio.h> int count = 0; int fib(int n) { if (n == 3) count++; if (n <= 2) return 1; else return fib(n - 1) + fib(n - 2); } int main() { int n = 0; scanf_s("%d", &n); int r=fib(n); printf("%d\n", r); printf("%d", count); return 0; }
注意:此题使用迭代法(非递归法)更好,因为递归法计算时,会有重复计算, 下图我们看到,
在计算第40个斐波那契数列时,第三个斐波那契数被重复计算了39088169次,这就会非常复杂。
而用迭代是,从小往大加就行了。