函数递归

发布于:2025-02-13 ⋅ 阅读:(131) ⋅ 点赞:(0)

目录

一、什么是递归?

 1.1递归的思想

1.2递归的限制条件

二、递归举例

2.1求n的阶乘

2.1.1分析和代码实现

2.2画图推演

​编辑

2.2顺序打印一个整数的每一位

2.2.1分析和代码实现

 2.2.2画图推演

3.递归与迭代

3.1求第n个斐波那契数


一、什么是递归?

递归是一种解决问题的方法,在C语言中,递归就是函数自己调用自己

#include<stdio.h>
int main()
{
	printf("hehe\n");
	main();//在main函数中又调用了main函数
	return 0;
}

以上是一个简单的递归程序,只不过上面的递归只是为了演示递归的基本形式,不是为了解决问题,代码最终会死递归,导致栈溢出(Stack overflow)。

 1.1递归的思想

将一个大型复杂问题层层转化为一个与原问题相似,但规模较小的子问题来求解;直到子问题不能再拆分,递归就结束了。所以递归的思考方式就是将大事化小的过程。

递归中递就是递推的意思,归就是回归的意思。

1.2递归的限制条件

在书写递归时,有两个必备条件

递归存在限制条件,当满足这个限制条件的时候,递归不再继续

每次递归调用后越来越接近这个限制条件

我们通过以下例子体会递归的两个限制条件

二、递归举例

2.1求n的阶乘

提示:一个正整数的阶乘是所有小于及等于该数的正整数的积,并且0的阶乘等于1。

题目:计算n的阶乘,就是1~n的数字相乘

2.1.1分析和代码实现

举例:

5!=5*4*3*2*1

4!=4*3*2*1

故5!=5*4!

 我们可以推出n!=n*(n-1)!

思路就是将一个大型复杂问题层层转化为一个与原问题相似,但规模较小的子问题来求解

当n==0时,n的阶乘是1,其余的就可以通过公式计算

我们可以写出函数fact求n的阶乘,假设fact(n)就是求n的阶乘,那么fact(n-1)就是求n-1的阶乘,函数如下

int fact(int n)
{
	if (n == 0)
		return 1;
	else
		return n * fact(n - 1);
}

 测试

int fact(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 a = fact(n);
	printf("%d\n", a);
	return 0;
}

运行结果

2.2画图推演

2.2顺序打印一个整数的每一位

输入一个整数,按照顺序打印整数的每一位

例如 输入1234 输出1 2 3 4

2.2.1分析和代码实现

如果n是一位数,n的每一位就是它自己;若n超过一位,就要拆分每一位

先1234%10==4,然后1234/10==123,相当于除掉了4

然后继续对123%10==3,然后/10去掉了3,以此类推

得到了1234的每一位,但是我们发现每一位是倒过来的

我们发现最低位是最容易得到的,假设我们写一个函数Print来打印每一位

Print(n)

假设打印1234,则表示为

Print(1234)      //表示打印1234的每一位

其中1234中的4可以通过%10得到

那么Print(1234)可以拆写成两步

Print(1234/10)     //打印123的每一位

printf(1234%10)  //打印4

完成以上两步,就得到1234的每一位

那么Print(123)又可以拆分为Print(123/10)+printf(123%10)

剩下的以此类推

直到打印的数字变成一位数,递归结束

#include <stdio.h>
void Print(int n)
{
	if (n > 9)
	{
		Print(n / 10);
	}
	printf("%d ", n % 10);
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	Print(n);
	return 0;
}

输出结果

 2.2.2画图推演

3.递归与迭代

递归是一种很好的编程技巧,但是也可能会被误用,就像求n的阶乘一样,看见推导公式很容易写成递归的形式。

int fact(int n)
{
	if (n == 0)
		return 1;
	else
		return n * fact(n - 1);
}

以上的函数可以产生正确结果,但是在递归函数调用的过程中会涉及一些运行时的开销

C语言中,每一次函数调用,都要为这次函数调用在栈区申请一块内存空间,用来为这次函数调用存放信息,这块空间叫做运行时堆栈或函数栈帧空间

若函数不返回,函数相应的栈帧空间就一直占用,所以函数调用存在递归调用的话,每次递归函数调用会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。

所有函数递归层次太深,会浪费很多栈帧空间,导致栈溢出

所以如果不想使用递归,就得想其他办法,通常使用迭代的方法(循环)

例如:计算n的阶乘

int fact(int n)
{
	int i = 0;
	int ret = 1;
	for (i = 1; i <= n; i++)
	{
		ret *= i;
	}
	return ret;
}
#include<stdio.h>
int main()
{
	int n = 0;
	scanf("%d", &n);
	int a = fact(n);
	printf("%d\n", a);
	return 0;
}

 使用以上代码完成任务,效率高比递归效果好。

3.1求第n个斐波那契数

举出更极端的例子,如求第n个斐波那契数,是不适合用递归求解的

我们分析斐波那契数的规律1 1 2 3 5 8 13 21 34 55可以总结出以下的公式可以通过递归描述

int Fib(int n)
{
	if (n <= 2)
		return 1;
	else
		return Fib(n - 1) + Fib(n - 2);
}

测试代码

int Fib(int n)
{
	if (n <= 2)
		return 1;
	else
		return Fib(n - 1) + Fib(n - 2);
}
#include<stdio.h>
int main()
{
	int n = 0;
	scanf("%d", &n);
	int a = Fib(n);
	printf("%d\n", a);
	return 0;
}

当我们输入50,发现需要很长时间才计算出结果,效率低,我们发现这个递归函数的效率无法接受,这是为什么呢?

通过分析我们发现随着递归不断展开,会发现有许多的重复计算,层次越深,冗余的计算越多。

我们更加证实了递归不是万能的,若我们使用迭代的方法

分析:我们知道斐波那契数前两个数都是1,然后前两个数相加就是第三个数

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;
}
#include<stdio.h>
int main()
{
	int n = 0;
	scanf("%d", &n);
	int a = Fib(n);
	printf("%d\n", a);
	return 0;
}

 用迭代的方法效率高很多。

总结:递归虽好,但有时也解决不了问题,不要过度迷恋。


网站公告

今日签到

点亮在社区的每一天
去签到