文章目录
一、什么是递归?
在代码运行中,有时候我们碰到冗长的代码无法进行下笔进行编码,这时候我们将会学习到应用函数递归进行运算。那么什么是递归呢?
1.1递归的思想
什么是函数递归?函数递归就是把大事化小,把小事在进行化小,直到解决问题。函数递归主要分为两个过程
一个叫递推,一个叫回归。
递推主要是将大事简化成一个个小事逐渐往下进行,回归就是把最小的事情解决然后逐渐传递给上一个值进行回归计算值,直到返回最初需要解决的问题。
1.2递归的限制条件
递归存在两种限制条件 :
1.递归存在限制条件, 递推不能无限一直递推下去,即递推存在限制条件:什么时候进入递归,递归什么时候结束,递归存在进入递归的条件和递归的结束条件。只有满足这个限制条件,递归将不会继续递归下去。
为什么函数递归不能无限递归的原因
在这里我简单写了一个递归函数,内存中分为几个不同的区域,在函数运行中产生的局部变量都会存放在内存中的栈区,接着我们看到函数,首先进入主函数main,然后我们会创建一个栈帧空间存储main函数中的变量,然后代码继续运行下去我们调用test函数,调用test函数会开辟一个栈帧空间,在test函数中再次进行调用test函数就会出现如上图一样的情况,内存中的栈区也是有一定空间的,每次调用函数都会额外开辟一份空间,如果一直无限调用下去栈区将会溢出。
2.每次递归调⽤之后越来越接近这个限制条件
因为每次递归,相当于都是一次新的函数调用,而每次函数调用系统必须给该函数划分栈帧空间,内部的递归函数没有退出,上层的递归就不能退出,栈帧就会累积许多块,如果累积超过栈的总大小,就会栈溢出。所以函数递归每次都需要逐渐的接近这个函数递归的停止条件,否则他就会无限一直递归下去。
提示:以下是本篇文章代码部分,下面案例可供参考
二、递归案例
2.1 案例1:求n的阶层
⼀个正整数的阶乘(factorial)是所有⼩于及等于该数的正整数的积,并且0的阶乘为1。
⾃然数n的阶乘写作n!。
题⽬:计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。
2.1.1分析
上面分析图解中,我们就要有递归的思想:把一个较大的事情转化为一个与原问题相似,但规模较小的问题进行求解。
当n==0的时候,n的阶乘是1,其余的阶乘都是可以用如上图一样的规律可以推出来的。
2.1.2 递归函数(Fact)的代码实现
int Fact(int n)
{
if(n==0)
return 1;
else
return n*Fact(n-1);
}
2.1.3 测试:main函数实现
#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 ret = Fact(n);
printf("%d\n", ret);
return 0;
}
2.1.4 运行结果和画图推演
注意此处不考虑n太大,n太大会存在溢出的情况,因为这是一个整型变量,存储的最大值65530
2.1.5 扩展:迭代方法求解n的阶乘
#include<stdio.h>
int Fact(int n)
{
int i = 0;
int sum = 1;
for (i = 1; i <= n; i++)
{
sum = sum * i;
}
return sum;
}
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Fact(n);>
printf("%d\n", ret);
return 0;
}
2.2 案例2:顺序打印⼀个整数的每⼀位
题目:输⼊⼀个整数m,打印这个按照顺序打印整数的每⼀位。
例如:
输入1234,打印1 2 3 4
输入4578,打印4 5 7 8
2.2.1分析
首先看到打印的第一步,我们想到的是怎么求出该数的每一位:
如果该数是一位数,那我们直接打印这一位即可。
如果该数超过一位数,那我们就得一个个求出该数的每一位。
假如我们要打印1234的每一位,我们得先求出他的每一位
1234 % 10 = 4,在这里我们得到了个位上的数4。 1234 / 10 = 123;
123 % 10 = 3, 在这里我们得到了十位上的数3。 123 / 10 = 12;
12 % 10 = 2,在这里我们得到了百位上的数2。 12 / 10 =1;
1 % 10 = 1 ,此时一位数我们直接打印即可。1 / 10 =0;由这里可以判断结束递归的条件是该数大于9.
但是这里发现最先得到的是个位上的数,因此我们这里弄出一个函数print
print(1234) = print(123) + printf(4);= print(1234/10) + printf(1234%10)
print(123) = print(12) + printf(3) + printf(4) ; = print(123/10) + printf(123%10)
print(12) = printf(1) + printf(2)+ printf(3) + printf(4) ;
2.2.2打印数(print)的每一位代码实现
void Print(int n) voidPrint(国际)
{
if(n>9) 如果(n>9)
{
Print(n/10); 打印(n/10);
}
printf("%d ", n%10); printf(“%d ”,n%10);
}
2.2.3 测试:print函数实现
#include<stdio.h>
void Print(int n)
{
if(n>9)
{
Print(n/10);
}
printf("%d ", n%10);
}
int main()
{
int m = 0;
scanf("%d", &m);
Print(m);
return 0;
}
2.2.4 运行结果和画图推演
2.3 案例3:求第n个斐波那契数
题目:求第n个斐波那契数;
输入: 5 输出:5
输入:10 输出:55
2.3.1分析
有不知道什么叫斐波那契数列的同学可以百度搜索详细了解一下,在这里小编就简单给大家介绍一下什么叫斐波那契数列。斐波那契数第n个数等于前两个数之和的相加,因此斐波那契数列第一第二个数都为1,以此推导剩下的斐波那契数:
1 1 2 3 5 8 13 21 34 55 。。。。
因此我们可以推导出求第n个斐波那契数的公式为:
2.3.2求第n个斐波那契数(fib) 的代码实现
int fib(int n)
{
if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
2.3.3 测试:fib函数实现
#include<stdio.h>
int fib(int n)
{
if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
int main()
{
int m = 0;
scanf("%d", &m);
int ret = fib(m);
printf("%d ",ret);
return 0;
}
2.2.4 运行结果和画图推演
2.1.5 扩展:迭代方法求解斐波那契数
在上面细心的人就会发现我们求解第50个斐波那契数,电脑突然间疯狂的转起来,但是始终等了很久也没有得到第50个斐波那契数的值,这是为什么呢?
由上面的图我们可以看出,当我们在进行递归运算的时候,他会重复计算很多的重复值,例如我们上面再递归求fib(49)需要求fib(48)和fib(47);而我们求fib(48)又会求一次fib(47)和fib(46),这时随着递归的层次原来越深,我们会发现我们会重复计算很多次重复的值,接下来我们计算一下算fib(3)一共计算了多少次。
#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 m = 0;
scanf("%d", &m);
int ret = fib(m);
printf("%d\n",ret);
printf("count = %d",count);
return 0;
}
这时我们可以发现fib(3)重复计算了39088619次,这大大加大了计算机运行的难度,因此求解斐波那契数的时候递归求解是一个错误的选择。因此我们可以直接采用迭代的求法。
#include<stdio.h>
int fib(int n)
{
int a = 1; //开始时第一个斐波那契数
int b = 1; //开始时第二个斐波那契数
int c = 1; //返回求解的斐波那契数,因为如果n<=2返回1所以c的初始值为1
while (n > 2)
{
c = a + b;
a = b;
b = c;
n--;
}
return c;
}
int main()
{
int m = 0;
scanf("%d", &m);
int ret = fib(m);
printf("%d\n",ret);
return 0;
}
2.4 案例4:递归实现n的k次方
模拟实现pow函数实现求解n的k次方
输入:2 3 输出:8
输入:3 3 输出:27
2.4.1分析
当k的值为0的时候,返回1;
当k的值为1的时候,返回n;
当k的值大于1的时候,返回n*mypow(k-1);
综上:
2.4.2 mypow函数实现
int mypower(int k, int n)
{
if (n == 0)
return 1;
else if(n >= 1)
return k * mypower(k, n - 1);
}
2.4.3 测试:主函数实现mypow函数
int mypower(int k, int n)
{
if (n == 0)
return 1;
else if(n >= 1)
return k * mypower(k, n - 1);
}
int main()
{
int k = 0;
int n = 0;
scanf("%d%d", &k, &n);
int ret = mypower(k, n);
printf("%d ", ret);
return 0;
}
2.4.4 运行结果
总结
通过上面案例我们初步了解了函数递归的思想 :把大事化小的特点。明白函数递归的充要条件是1,首先要有限制条件,即进入递归的条件和结束递归的条件。2,在函数递归的时候我们要逐渐的接近这个递归条件。
当然函数递归的学习远不如于此,需要大家在不断的实践练习中不断了解函数递归的妙用。这是小编对于函数递归的理解如果有读者有看不懂的地方或者更好的建议欢迎评论下方留言。