函数递归的介绍

1.递归的定义

C 语言中,递归就是函数自己调用自己

上面的代码就是 main 函数在函数主体内 自己调用自己

但是,上面的代码存在问题:main 函数反复地 自己调用自己 ,不受限制,停不下来。

最终形成死递归,导致栈溢出

1.0栈溢出

每一次函数调用,都要从内存上的栈区为这次函数调用分配内存空间

栈区的大小是有限的,如果无限的调用函数就会导致栈区空间被使用完,

从而发生栈溢出的现象,导致程序运行停止

1.1递归的思想

将一个大问题细分为一个个子问题,直到子问题不可拆分为止就是递归的思想。

递归就是将大事化小的过程。

递,就是递推;归,就是回归。

1.2.递归的书写条件

(1)递归存在限制条件,当条件满足时,递归便不再继续

(2)每次递归调用之后,越来越接近这个限制条件

2.举例

2.1阶乘

我们知道,n! = n * (n-1) * (n-2) * (n-3) * ......* 2 * 1

又注意到,2! = 2 * 1! || 3! = 3 * 2! || 4! = 4 * 3!

所以 n! = n * (n -1)!

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

当 n = 3 时

Fact1(3) = 3*Fact1(2)

Fact1(2) = 2*Fact1(1)

Fact1(1) = 1*Fact1(0)

Fact1(0) = 1

先是递推,每次函数调用之后,接近 n 等于 0 这个限制条件

然后回归

Fact1(0) = 1

Fact1(1) = 1*Fact1(0) = 1

Fact1(2) = 2*Fact1(1) = 2

Fact1(3) = 3*Fact1(2) = 6

2.2顺序打印整数的每一位

运用递归时,我们要相信我们所创建的函数能够完成相应的任务

例如,顺序打印 123 中的每一位

void Print1(int n)
{
	if (n > 9)
		Print1(n / 10);

	printf("%d ", n % 10);
}

在上面的代码实现中:

Print(123)中 123 > 9 所以 先调用函数**Print(123/10)**打印 12 再打印3

Print(123 / 10)中 12 > 9 所以 先调用函数**Print(12/10)**打印 1 再打印2,

Print(12 / 10)中 1 < 9 开始 打印 1,函数**Print(12 / 10)**调用完毕

然后返回函数Print(123 / 10) 打印 2, 再返回函数**Print(123)**打印3

Print(123) -> Print(123 / 10) -> Print(12 / 10)

这是递推, 将 大问题转化位 小问题, 只打印个位数

Print(12 / 10) - > Print(123 / 10) -> Print(123)

这是回归 ,因为只有函数调用任务实现后,才会继续后面的操作

2.3斐波那契数列

1,1,2,3,5,8,13,21,34........

第一二个数为1,其余的数等于前两个数相加,就是斐波那契数列的规律

代码实现:

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

例如 n = 4

Fib(4) = Fib(3) + Fib(2)

Fib(3) = Fib(2) + Fib(1)

Fib(2) = 1

这是递推,然后回归

Fib(3) = 1 +Fib(1)

Fib(1) = 1

所以Fib(3) = 1+ 1 = 2

继续回归

Fib(4) = 2 + Fib(2)

Fib(2) = 1

Fib(4) = 2 +1 = 3

注意:只用当前一个Fib函数调用完毕后才会调用后面的Fib函数

3.递归与迭代

上述例子用递归的思想实现时,代码较为简洁

但不可避免的是,如果递归的层次太深,栈溢出的现象就不可避免

所谓层次太深,就是说许多函数被调用后,未被销毁,依然占据着栈区的部分内存

最终导致栈区空间被使用完,出现栈溢出的现象

虽然存在限制条件 n > 10000 但是 3988次调用之后,程序就自动停止了 ,

因为递归层次太深,出现了栈溢出的现象

注意到,Fib函数虽然被调用了很多次,但是却没有出现栈溢出的现象

这是因为Fib(40)最深只会调用40次,然后就会被销毁,再继续后面的调用

..........................................................................................................................................

注意到,Fib(40)重复调用的Fib函数太多,运行效率非常低

此时就可以采用迭代的方法实现斐波那契数列。

循环是一种迭代,迭代不仅仅是循环

int Fact2(int n)
{
	int a = 1;//前两个数
	int b = 1;//前一个数
	int c = 1;//当前的数
 

//当n 小于等于 2时
//直接返回c 为 1
//当n 大于 2时
//1 + 1 = 2  1+ 2 = 3 2+ 3 = 5
//第n个数需要迭代n-2次

	for (int i = 0; i < n - 2; ++i)
	{
		c = a + b;
		a = b;
		b = c;
	}

   return c;

}

运行一下就知道,迭代实现斐波那契数列比递归更快!

但是解决实际问题时

递归比较好想,如果能保证不会出现栈溢出等问题就可优先考虑使用递归

其次是迭代

相关推荐
fai厅的秃头姐!6 小时前
C语言03
c语言·数据结构·算法
life_time_6 小时前
C语言(22)
c语言·开发语言
鱼嘻8 小时前
Linux自学day23-进程和线程
linux·服务器·c语言·进程和线程
BUG 劝退师9 小时前
C语言预处理学习笔记
c语言·笔记·学习
小立爱学习10 小时前
Linux 内核自旋锁spinlock(四)--- queued spinlock
linux·c语言
我爱蛋蛋后12 小时前
Linux驱动开发之音频驱动与基础应用编程
linux·c语言·驱动开发·音视频
卷卷的小趴菜学编程13 小时前
c++之多态
c语言·开发语言·c++·面试·visual studio code
大白的编程日记.14 小时前
【C++笔记】C+11深度剖析(三)
c语言·开发语言·c++
和光同尘@15 小时前
56. 合并区间 (LeetCode 热题 100)
c语言·开发语言·数据结构·c++·算法·leetcode·职场和发展
YH_DevJourney16 小时前
Linux-C/C++《C/9、信号:基础》(基本概念、信号分类、信号传递等)
linux·c语言·c++