C语言——函数递归与迭代

 各位CSDN的uu们大家好呀,今天将会给大家带来关于C语言的函数递归的知识,这一块知识理解起来稍微会比较难,但是它也是真的很好用!

话不多说,让我们开始今天的内容吧!

目录

1.函数递归

1.1 什么是递归?

1.2 递归的两个必要条件

1.3 递归实例练习

2.函数的迭代

2.1 什么是迭代

2.2 递归与迭代实例

2.2.1 求第n个斐波那契数

2.2.2 实现n的阶乘

3.总结


1.函数递归

1.1 什么是递归?

递归是程序调用自身的一种编程技巧,也是在程序设计语言中被广泛应用的一种算法,一个过程或函数在其定义或说明中有直接或间接调用自身的方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,这样一来,只需少量的程序就可以描述出解题过程所需要的多次重复计算,大大减少程序的代码量。

递归的思考方式:把大化小

1.2 递归的两个必要条件

·在使用递归时一定要设定限制条件,当满足这个限制条件时,递归就不再继续;

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

1.3 递归实例练习

1.接收一个无符号整型值,按顺序打印它的每一位

如:输入1234 打印1 2 3 4

代码实现如下:

#include<stdio.h>

void print(int n)
{
	if (n > 9)
	{
		print(n/10);
	}
	printf("%d ", n % 10);
}


int main()
{
	int num = 0;
	scanf("%d", &num);
	print(num);
	return 0;
}

我们来看一下输入1234后的运行结果:

 

 可以看到,运行结果符合我们的需求。现在让我们一起来阅读这段代码,看看函数的递归体现在哪里:

我们在main函数中用scanf对定义的num变量进行输入赋值,并调用print函数,将num的值传给print,完成了main函数的编写,我们回到main函数前面(函数先声明(定义)后使用),开始print函数的实现;

这里print函数的实现通过画图给大家讲解(很重要!!!):

2.函数的迭代

2.1 什么是迭代

迭代是函数利用自身已有的变量来推算需要的新变量,和递归直接调用自身存在一定差异,二者在某些情况下是可以相互转换的,同种情况下,这两种方法优先考虑迭代,因为递归很容易造成栈溢出,下面就通过两个实例来感受一下迭代与递归的差异。

2.2 递归与迭代实例

2.2.1 求第n个斐波那契数

斐波那契数列是一个特殊的数列,从第三项开始,每一项都是前两项之和,

1,1,2,3,5,8,......,(n-2),(n-1),(n-2)+(n-1)

现在我们用递归的方法实现一下:

#include<stdio.h>
//递归实现求第n个斐波那契数
int fib(int n)
{
	if (n <= 2)
		return 1;
	else
		return fib(n - 2) + fib(n - 1);
}



int main()
{
	int n = 0;
	scanf("%d", &n);
	printf("%d", fib(n));
	return 0;
}

但事实上,用递归求第n个斐波那契数会出现很多重复的计算,我们可以通过画图来看一下:

假设我们要求第50个斐波那契数: 

那么就要调用fib函数求第49个和第48个,求第49个和第48个又要再调用fib函数求第48、47、46个,其中第47个还调用了两次,越往下存在的重复计算越多,这样即浪费内存空间,又可能造成栈溢出。

为了让大家更清晰地感受到求第n个斐波那契数需要调用fib函数的次数,我们在原先代码基础上改进一下:

#include<stdio.h>
int count = 0;
int fib(int n)
{
//这里计算的是第三个斐波那契数被重复计算多少次
	if (n == 3)
		count++;
	if (n <= 2)
		return 1;
	else
		return fib(n - 2) + fib(n - 1);
}



int main()
{
	int n = 0;
	scanf("%d", &n);
	/*printf("%d\n", fib(n));*/
	printf("%d", count);
	return 0;
}

我们来求一下第40个斐波那契数,运行一下看看count的值是多少:

 可以看到,count的值高达39088169,也就是说第三个斐波那契数被重复计算了39088169次。

为了避免这种不必要的大量重复计算,我们采用了非递归的形式改进代码,也就是迭代,代码实现如下:

#include<stdio.h>

int fib(int n)
{
	int result;
	int pre_result;
	int next_older_result;
	result = pre_result = 1;
	while (n > 2)
	{
		n--;
		next_older_result = pre_result;
		pre_result = result;
		result = pre_result + next_older_result;
	}
	return result;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = fib(n);
	printf("%d", ret);
	return 0;
}

可以看到,在fib函数里,程序通过while循环不断使用两个已知变量求第三个需要的变量,这样的方式要比直接调用函数自身(即递归)减少很多重复计算。

2.2.2 实现n的阶乘

#include<stdio.h>

//递归实现
int factorial(int n)
{
	if (n <= 1)
		return 1;
	else
		return n * factorial(n - 1);
}


//迭代实现
int  factorial(int n)
{
	int result = 1;
	while (n > 1)
	{
		result *= n;
		n--;
	}
	return result;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	printf("%d", factorial(n));
	return 0;
}

实现n的阶乘和求第n个斐波那契数也是同样的道理,uu们可以自行探究一下,用递归求n的阶乘,会造成多少重复计算。

3.总结

我们要知道,系统分配给程序的空间是有限的,如果出现了死循环或者是死递归,就可能导致系统一直不停地开辟栈空间,最终耗尽栈空间,造成栈溢出的现象。

许多问题以递归的形式来解释,会让问题变得更清晰易懂,但这些问题的迭代实现往往要比递归实现效率更高,但同样的,迭代实现的代码理解起来没有递归那么容易。

在能用迭代的情况下,尽量不用递归,除非该问题的迭代实现非常复杂,那么此时递归实现的简洁性就足以弥补它运行时所产生的一系列问题。

这篇文章就到这里啦,关于递归与迭代的知识,还需要uu们多去找题练习,递归虽好,但不要经常用哦。

下篇文章会给大家讲解一下关于递归应用的汉诺塔问题以及青蛙跳台阶问题,帮助大家更好地理解递归的知识。

如果你觉得这篇文章对你有帮助的话,麻烦动动小手为我点个赞哦,你的喜欢就是我创作的动力!

  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

code菜只因

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值