1.什么是递归
是一种解决问题的方法,在C语言中,递归就是函数自己调用自己
写一个史上最简单的递归
#include<stdio.h>
int main()
{
printf("hehe\n");
main();
return 0;
}
上面的代码算是递归,但是是一个错误的释放,这里导致栈溢出 ,代码最终也会陷入死递归,导致栈溢出(Stack overflow)
每一次函数调用都会占有一块内存空间:函数栈帧空间(运行时堆栈)
在栈区上申请的空间放满
放满时溢出
所以函数是不能这样无限递归下去的,递归必须是有条件的
递归的思想
把大型复杂问题转化成一个与原问题相似,但规模较小的子问题来求解;知直到子问题不能再被拆分,递归就结束了
递归的思考方式就是把大事化小的过程
递就是递推,归就是回归
递归的限制条件
书写时的两个必要条件:
递归存在限制条件,当满足这个限制条件时,递归便不再继续
每次递归调用之后越来越接近这个限制条件
3.递归举例
举例1:求n的阶乘
一个正数的阶乘是所有小于等于该数的正整数的积
0的阶乘为0
题目:计算n的阶乘(不考虑溢出),
分析和代码实现
n的阶乘为n!=n*(n-1)!,这样的思路就是把一个较大的问题,转换为一个与原问题相似,但规模较小的问题来求解的
当n==0时,n的阶乘是1,其余n的阶乘都可以通过公式计算
n的阶乘递归公式如下:
int Fact(unsigned int n)
{
if (n == 0)
return 1;
else
return n * Fact(n - 1);
}
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Fact(n);
printf("%d\n", ret);
return 0;
}
1.递归的条件是:n>0
递归停下来的条件是n==0
2.不断地逼近跳出条件
一个问题有多种解决办法:
1.递归
2.非递归
递归的特点:
使用少量的代码,完成复杂的任务
画图推演
举例2:顺序打印一个整数的每一位
分析和代码实现
一个数字的最低为是最容易得到的,通过%10就能得到
画图推演
每次调用完后再执行
1.递归的停止条件是n<=9
2.n=n/10(每次都在逼近停止条件)
void print(int n)
{
if (n > 9)
{
print(n / 10);
printf("%d ", n % 10);
}
else
{
printf("%d ", n % 10);
}
}
#include<stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
print(n);//print函数用来打印n的每一位
return 0;
}
4.递归与迭代
函数在调用的过程中,每一次函数调用都得申请一次空间
调用完后会还给操作系统
递归是一种很好的编程技巧,但是和很多技巧一样,也是可能被误用的,就像举例1一样,看到推导公式,很容易就写成递归的形式
int Fact(unsigned int n)
{
if (n == 0)
return 1;
else
return n * Fact(n - 1);
}
Fact函数是可以产生正确的结果,但是在递归函数调用的过程中,涉及一些运行时的开销
在C语言中每一次函数调用,都需要为本次函数调用在内存的栈区申请一块内存空间来保存函数调用期间的各种局部变量的值,这块空间被称为运行时堆栈,或函数栈帧
函数不返回,函数对应的栈帧空间就一直占用,所以如果函数调用中存在递归调用的话,每一次递归调用都会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间
所以如果采用函数递归的方式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(stack overflow)的问题
如果不想使用递归就得想其他办法,通常就是迭代的方式(通常就是循环的方式,循环是迭代的一种 )
比如:计算n的阶乘
int Fact(int n)
{
int i = 0;
int ret = 1;
for (i = 0; i <= 0; i++)
{
ret *= i;
}
return ret;
}
上述代码是能够完成任务,并且效率是比递归的方式更好
不是必须通过递归来实现
举例3:求第n个斐波那契数
斐波那契数列:
1 1 2 3 5 8 13 21 34 55 ... 前两个加起来等于第三个
求第n个斐波那契数:Fib(n)
int count = 0;
int fact(a)
{
if (a == 3)
count++;
if (a <= 2)
return 1;
else
return fact(a - 1) + fact(a - 2);
}
#include <stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
int sum = fact(a);
printf("%d", sum);
printf("%d", count);
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/2252320db8a4373e1ee043e933e767b9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/24f61c1b1a7e0a3d4b6397e7c83b37bc.png)
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;
}
尾递归的方式,就不存在刚才的效率问题了
研究一下尾递归,尾递归如何求第n个斐波那契数
在一些新的编程语言中,编译器或者解释器,会自动优化递归,直接给你改成尾递归的方式
拓展学习:
![](https://i-blog.csdnimg.cn/blog_migrate/d33012e297f2fadcfaae98ee463f0975.png)
![](https://i-blog.csdnimg.cn/blog_migrate/99dfb000ec9435183bd5afc9b5cfd3f4.png)