C语言函数递归

目录

1.前言

2.何为递归 

3.递归的限制条件

4.递归的思想

 5.递归举例

5.1小乐乐走台阶

5.2斐波那契数列

 6.递归与迭代

  6.1递归的弊端

6.2斐波那契数列(迭代篇 )

7.总结


1.前言

学完函数后,不知各位是否和我一样有种意犹未尽的感觉。如果有,或许是因为它——函数递归,这个学习函数怎么也绕不开的章节,这个让程序员开心,让计算机难受的算法.......那么事不宜迟,赶紧开启下一程,一了我们的夙愿。

2.何为递归 

C语言中的递归,说白了就是 函数自己调用自己 。哦,既然是函数自己调用自己,那我们就来写个粗糙点的代码呗:
void test()
{
	printf("hello world!\n");
	test();//test函数自己调用自己
}
int main()
{
	test();
	return 0;
}

运行起来看看:

 可为啥好端端的函数递归最后变成死循环了呢?说明函数递归并不是简单的函数自己调用自己,还必须要有限制条件

3.递归的限制条件

要想写出正确的函数递归,有两个必要不充分条件:                                                                                         ① 递归存在限制条件,当满足这个限制条件的时候,递归便不再继续。                                               ②每次递归调用之后越来越接近这个限制条件                                                                                        即包含这两个限制条件的函数递归不一定正确,但是没有这两个限制条件的递归就一定是错的。

我们将上面的函数递归加上这两个限制条件 :

void test(int n)
{
	if (n > 0)//限制条件
	{//每一次递归n的值都更加接近0直到不满足该条件
	   printf("hello world!\n");
		test(n-1);//每一次递归都使n减1
	}
}
int main()
{
	int n = 3;//打印3次 hello world!
	test(n);
	return 0;
}

这样就舒服多了。

4.递归的思想

  把⼀个大型复杂问题层层转化为⼀个与原问题相似,但规模较小的子问题来求解;直到子问题不能再被拆分,递归就结束了。所以递归的思考方式就是把 大事化小的过程
接下来我们通过几道经典的题目来慢慢体会。

 5.递归举例

   递归求解核心思想: 一个问题直接求解时不好求解,如果可以将其划分成其子问题,并且子问题和原问题有相同的解法时,就可以使用递归的方式解决

5.1小乐乐走台阶

牛客网中有这样一道题目: 

  我们把自己想象成小乐乐,征服整个台阶就是我们的最终目标,那么有多少种方法来征服它呢?当n=1或者n=2时,目标过于简单,掐指一算,我们有一种和两种方法征服。当n过大时我们无法一步登顶,那就把它拆成一个的战略小目标和余下的大目标。此时我们不妨从n=3入手,第一个战略目标:先走一步看看,这样我们我有上一阶台阶和上两阶台阶两种选择。剩下的战略目标就有走完一阶或走完两阶两种,这不就到了我们掐指一算的时候吗?所以征服3阶台阶的方法数=征服两阶台 方法数+征服一阶台阶的方法数。

            那么这样一来n=5时不就是n=4时的加上n=3时的吗。那么n时的就是n-1时的和n-2时的方法之和 。那么n-1时的就可以拆成n-2时的和n-3时的方法之和.......以此类推,直到拆成我们可以掐指一算的n=1和n=2时即可。

我们把大问题n拆成子问题n-1和n-2,并且子问题和大问题一样可以拆成更小的子问题,这不就是递归的核心思想吗?

那么代码来:

#include <stdio.h>
//int count = 0;
int step(int n)
{
	if (n <= 2)//当我们掐指一算时递归停止
	{
		if (1 == n)
			return 1;
		if (2 == n)
			return 2;
	}
	/*if (n == 5)
		count++;*/
	if (n > 2)
	{//将n时的拆成n-1时的和n-2时的方法之和
		return (step(n - 1) + step(n - 2));
		//函数递归时所传递的参数越来越接近限制条件
	}
	return 0;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	if (n > 0)
		step(n);
	else
		printf("输入错误\n");
	printf("%d\n", step(n));
	//printf("第五项计算了%d次", count);
	return 0;
}

                        

5.2斐波那契数列

 提到斐波那契数列相信大家都不会很陌生,它的规律是从第三项开始,该项上的数等于前两项之和。  1,1,2,3,5,8,13..... 那么我们来写一个代码实现求解任一项斐波那契数列的值。

思路:谈及它的规律——该项上的数等于前两项之和,即求解第n项斐波那契数列时,将其拆成第n-1项和第n-2项之和。这不就求解我们上一题时所遇到的情况吗?好家伙,竟然直接撞枪口上了,那也就不装了,直接开干:

int count=0;
int F(int n)
{
	/*if (5 == n)
		count++;*/
	if (n <= 2)
		return 1;
	else
		return F(n - 1) + F(n - 2);
	//每一次递归所传递的参数都更加接近限制条件
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = F(n);
	printf("第%d项为%d\n",n, ret);
	//printf("第五项共循环了%d次\n", count);
	return 0;
}

 

 6.递归与迭代

  6.1递归的弊端

递归是一种很好的编程技巧,因为只需要寥寥几行代码,就可以较为轻易地解决我们使用循环解决感到困难的问题。但是递归也有它致命的弊端,那就是代码的运行效率并不高。究其主要原因,就是因为有些数据没有很好地利用起来,反而计算了很多次。

  以上面的斐波那契数列为例,当所求的n比较大时,第五项的值就会被重复计算多次。我们取消计算该次数的注释,运行起来看看:

 隔着屏幕都能感觉到计算器的温度。所以当我们的需要递归地比较深时,运行效率会以肉眼可见的速度下降。这时我们就果断放弃使用递归,改用迭代的方式求解。

6.2斐波那契数列(迭代篇 )

 所谓迭代,就是为了逼近目标结果,重复反馈过程的活动。循环语句就是一种迭代。

那该怎么用循环语句实现求解对应项的数的代码呢?我们就从它的底层逻辑出发。既然从第三项开始,每一项等于前两项之和,那么我们就设置三个变量a,b,ret用来表示相邻两项的值,以及他们的和。每进行一次,b的值赋给a,ret的值赋给b,新一轮a与b的和赋给ret,以此类推。

int main()
{
	int n = 0;
	int ret = 0;
	int a = 1;
	int b = 1;
	scanf("%d", &n);
	if (1 == n || 2 == n)
		ret = 1;
	else
	{
		while (n-2)//确保n>=3
		{
			ret = a + b;
			a = b;
			b = ret;
			n--;
	    }
	}
	
	printf("%d", ret);
	return 0;
}

 

这样计算n较大时代码运行起来的效率就明显提高了!

7.总结

函数递归第一个非常实用的技巧,合理地运用会使代码更加简洁的,思考问题多一个方向。但不应该“迷信递归”,当递归层次较深,应该果断放弃,进而选择迭代等运行效率更高的算法。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值