函数递归详解

本文详细介绍了递归的概念,递归思想的应用,以及递归在C语言中的实例,包括求阶乘和顺序打印整数。同时讨论了递归的限制条件和递归与迭代的区别,以斐波那契函数为例说明递归可能导致的重复计算问题及优化策略。
摘要由CSDN通过智能技术生成

1.什么是递归

递归就是函数自己调用自己,现在给大家写一个最简单的C语言递归代码:

#include <stdio.h>
int main()
{
	main();//main函数中调用main函数
	return 0;
}

上述代码就是一个最简单的递归程序,但是上面的递归知识为了演示递归的基本形式,如果运行的话会陷入死递归,最后栈溢出。

1.2递归的思想

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

下面具体解释一下递归中的递和归都是什么意思。

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

打个比方,你在做一道数学证明题。你想要证明一道题目可以层层反推直到推到已知条件,这便是一次次的传递,之后你再从题目条件一步步证得答案,这便是回归。

2.递归的限制条件

我们继续利用数学证明题来思考递归需要什么?首先,我们一步步反推一定会推到已知条件,否则我们的反推则永无止境。因此,递归也亦然,他需要一个条件作为它的终止条件,否则递归也永无止境。我们还可以继续思考,一道数学题目层层反推会越来越逼近已知条件,而我们的递归呢?它也一定会越来越逼近他的终止条件。

因此,我们可以得到如下结论:1.传递存在限制条件,当满足这个限制条件的时候,则传递不会再继续传递,接下来则会进行回归。

                                                  2.每次传递之后都会越来越接近这个限制条件。

  下面我们通过举例来体会这两个限制条件。

3.递归的举例

3.1求n的阶乘

 刚刚已经讲解过,递归即是将一个大问题化为一个个子问题,我们又讲了,递归存在限制条件,而且每次递归都会接近这个条件。

那么我们现在的工作即将这个问题化为一个个小问题,然后找出一个限制条件。

现在我们来分解一下这个问题:5!=5*4!---------->传递4

                                                  4!=4*3!---------->传递3

                                                  3!=3*2!---------->传递2

                                                  2!=2*1

通过分解这个问题,我们可以发现,最终的限制条件是得到“1”,而每次的传递,传递的是蓝色数字。

在思考好上述的代码逻辑之后,我们来实现这个问题。

int Fact(int n)
{
	if (n == 1)
	{
		return 1;
	}
	else return n * Fact(n - 1);
}
int main()
{
	int n = 0;
	scanf_s("%d", &n);
	int ret = Fact(n);
	printf("%d\n", ret);
	return 0;
}

给大家画图推演一下

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

输⼊⼀个整数m,打印这个按照顺序打印整数的每⼀位。
⽐如:
输⼊:1234  输出:1  2  3  4
输⼊:520    输出:5  2  0

下面我们来思考一下实现方法。

很容易想到,如果n是⼀位数,n的每⼀位就是n⾃⼰,n是超过1位数的话,就得拆分每⼀位。
1234%10就能得到4,然后1234/10得到123,这就相当于去掉了4
然后继续对123%10,就得到了3,再除10去掉3,以此类推,不断的 %10 和 /10 操作,直到1234的每⼀位都得到;但是这样解决问题的话,得到的数字顺序是倒着的。但是我们发现,数字的最低位可以直接通过%10来得到。我们先假设解决这个问题的函数叫Print,下面我们来将大事化小。

//我们假设解决这个问题的函数叫Print
Print(1234)//4通过%10得到,那么得到123的方法就是/10;
Print(1234/10)//3通过%10得到,那么得到12的方法就是/10
Print(123/10)//2通过%10得到,那么得到1的方法就是/10
Print(12/10)

我们将这个问题化为了子问题来解决,我们发现当Print函数传递的数值为个位数时,这个递归会由递至归。也就是说,这个递归的限制条件是函数内参数为个位数。因此,我们可以得到如下代码。

void Print(int n)
{
	if (n > 9)
	{
		Print(n / 10);
	}
	printf("%d ", n%10);//每次都打印%10
}

4.递归与迭代

递归是⼀种很好的编程技巧,但是很多技巧⼀样,也是可能被误⽤的,就像举例1⼀样,看到推导的公式,很容易就被写成递归的形式,但是使用循环的方法也一样可以解决这个问题。

int Fact(int n)
{
int i = 0;
int ret = 1;
for(i=1; i<=n; i++)
{
ret *= i;
}
return ret;
}


上述代码是能够完成任务,并且效率是⽐递归的⽅式更好的。下面我们来举个例子。

4.2斐波那契函数

计算第n个斐波那契函数是不适合使用递归的方法实现的,但是也可以通过递归解决。下面给出斐波那契函数的解决途径。

看到上述的公式,我们很容易写出斐波那契函数的递归函数。

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

但是如果n输入的数字为50的话,需要计算机花费很长的时间才可以计算出结果。这个计算锁花费的时间是非常长的,这是因为它按照下图的方式进行计算。

可以看到,相同颜色的字块表明同一个函数,而同一个函数计算了多次,因此计算机进行了大量的重复计算,这就导致我们的程序运行时间很长。我们可以写程序来计算出其中的一个子问题所代表的函数计算了多少次。

#include <stdio.h>
int count = 0;//因为count变量写在函数内部会被销毁,因此需要将count定义为全局变量。
int Fib(int n)
{
	if (n == 3)//计算Fib(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 ret = Fib(n);
	printf("%d\n", ret);
	printf("计算了:%d\n", count);
	return 0;
}

可以看到,当计算第二十个斐波那契函数时,Fib(3)计算了2584次,多次的重复计算大量的占用了计算资源。因此,使用递归的方法解决这个问题很不明智。

#include <stdio.h>
int main()
{
	int a = 1;
	int b = 1;
	int c = 1;
	int n = 0;
	scanf("%d", &n);
	while (n > 2)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值