首页 > 其他分享 >函数递归

函数递归

时间:2024-10-26 11:52:16浏览次数:3  
标签:return 函数 递归 int printf print fact

函数递归

目录​

  1. 什么是递归
  2. 递归的限制条件
  3. 递归的举例
  4. 递归与迭代

1. 递归是什么?​

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

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

写一个最简单的C语言递归代码:

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

在这里插入图片描述

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

递归的思想:

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

2. 递归的限制条件​

递归在书写的时候,有2个必要条件:

  • 递归存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用之后越来越接近这个限制条件

3. 递归举例​

3.1 举例1:求n的阶乘​

计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。​

3.1.1 分析和代码实现

我们知道n的阶乘的公式:​n!= n ∗(n −1)!​

这样的思路就是把一个较大的问题,转换为一个与原问题相似,但规模较小的问题来求解的。

n!—> n*(n-1)!

(n-1)! —> (n-1)*(n-2)!​

…​

直到n是1或者0时,不再拆解​

定义函数 fact(n)

分类讨论:

  1. n ≤ 0: fact(n) = 1
  2. n > 0: fact(n) = n * fact(n-1)
#include<stdio.h>
int fact(int n)
{
	if (n >= 1)
		return n * fact(n - 1);
	return 1;
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int a = fact(n);
	printf("%d", a);
	return 0;
}

3.1.2 画图推演
在这里插入图片描述

3.2 举例2:顺序打印一个整数的每一位​

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

比如:

输入:1234 输出:1 2 3 4 ​

输入:520 输出:5 2 0​

3.2.1 分析和代码实现

这个题目,放在我们面前,首先想到的是,怎么得到这个数的每一位呢?

之前的思路是这样的:

#include<stdio.h>
int main()
{
	int num = 0;
	int a = 0;
	scanf("%d", &num);
	while (num != 0)
	{
		a = num % 10;
		num = num / 10;
		printf("%d ", a);
	}
	return 0;
}

但是这里有个问题就是得到的数字顺序是倒着的。

但是我们有了灵感,我们发现其实一个数字的最低位是最容易得到的,通过 %10就能得到。

那我们假设想写一个函数Print来打印n的每一位,如下表示:​

定义函数 print

print(1234)

==>print(123) + printf(4)

==>print(12) + printf(3)

==>print(1) + printf(2)

==>printf(1)

直到被打印的数字变成一位数的时候,就不需要再拆分,递归结束。分类讨论:

分类讨论

  1. n≤9(n为个位数):printf(”%d”, n)

  2. n ≥10:

     print(n - 1) 
    
     printf(”%d”, n % 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;
}

//方法二
#include<stdio.h>
void print(int n)
{
	if (n <= 9)
	{
		printf("%d ", n);
		return;
	}
	print(n / 10);
	printf("%d ", n % 10);
}

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

3.2.2 画图推演

在这里插入图片描述

4. 递归与迭代​

4.1 迭代

递归是一种很好的编程技巧,但是很多技巧一样,也是可能被误用的。

在C语言中每一次函数调用,都要需要为本次函数调用在栈区申请一块内存空间来保存函数调用期间的各种局部变量的值,这块空间被称为运行时堆栈,或者函数栈帧

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

所以如果采用函数递归的方式完成代码,**递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(stack overflow)**的问题。

在举例1中,Fact函数是可以产生正确的结果,但是在递归函数调用的过程中涉及一些运行时的开销。所以如果不想使用递归就得想其他的办法,通常就是迭代的方式(通常就是循环的方式)。

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

迭代是能够完成任务,并且效率是比递归的方式更好的。

事实上,我们看到的许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更加清晰,但是这些问题的迭代实现往往比递归实现效率更高

当一个问题非常复杂,难以使用迭代的方式实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销

4.2 举例3:求第n个斐波那契数

我们也能举出更加极端的例子,就像计算第n个斐波那契数,是不适合使用递归求解的,但是斐波那契数的问题通过是使用递归的形式描述的,如下:

在这里插入图片描述

看到这公式,很容易将代码写成递归的形式,如下所示:

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

当我们n输入为50的时候,需要很长时间才能算出结果,这个计算所花费的时间,是我们很难接受的,这也说明递归的写法是非常低效的。

其实递归程序会不断的展开,在展开的过程中,我们很容易就能发现,在递归的过程中会有重复计算,而且递归层次越深,冗余计算就会越多

在这里插入图片描述

我们可以做一个测试:

#include<stdio.h>
int count = 0;
int fib(int n)
{
	if (n <= 2)
		return 1;
	if (n == 3)
		count++;
	return fib(n - 1) + fib(n - 2);
}
int main()
{
	int n = 0;
	scanf("%d", &n);//输入 40
	int a = fib(n);
	printf("%d\n", a);//输出 102334155
	printf("%d", count);//输出 39088169
	return 0;
}

这里我们看到了,在计算第40个斐波那契数的时候,使用递归方式,第3个斐波那契数就被重复计算了39088169次,这些计算是非常冗余的。所以斐波那契数的计算,使用递归是非常不明智的,我们就得想迭代的方式解决。

#include<stdio.h>
int fib(int n)
{
	int a = 1;
	int b = 1;
	int c = 1;//n = 1或2时的返回值
	while (n > 2)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int a = fib(n);
	printf("%d\n", a);
	return 0;
}

迭代的方式去实现这个代码,效率就要高出很多了。

标签:return,函数,递归,int,printf,print,fact
From: https://blog.csdn.net/encoconut/article/details/143251469

相关文章

  • time函数
    一、导包importname二、函数time.time()     功能:返回一个时间戳从1970年1月1日00:00:00UTC到当前时刻的秒数,这个时间戳是一个浮点数。 time.sleep(seconds)   功能:让程序暂停执行指定的秒数。time.localtime([secs])   功能:将一个......
  • python内置函数大全
    文章目录一、数学运算相关二、类型转换相关三、序列操作相关四、对象操作相关五、反射操作相关六、输入输出相关七、文件操作相关八、代码编译执行相关九、装饰器相关十、其他Python的内置函数是Python提供的一系列可以直接使用的函数,这些函数涵盖了数学运算、类型......
  • 【Python中的匿名函数】如何高效使用lambda表达式!
    Python中的匿名函数:如何高效使用lambda表达式Python中的匿名函数,也被称为lambda表达式,是一种简洁的函数定义方式。它们在某些场景中能够显著简化代码结构,提升可读性和代码执行效率。本文将详细讨论lambda表达式的使用方法、优缺点、适用场景以及使用技巧,帮助你更高效地应用......
  • 网卡工具类 - C#小函数类推荐
          此文记录的是网卡操作的工具类。/***网卡工具类AustinLiu刘恒辉ProjectManagerandSoftwareDesignerE-Mail:[email protected]:http://lzhdim.cnblogs.comDate:2024-01-1515:18:00使用方法:1、获取......
  • 详解c++中的set_difference函数
    set_difference功能描述:求两个集合的差集函数原型:set_difference(iteratorbeg1,iteratorend1,iteratorbeg2,iteratorend2,iteratordest);//求两个集合的差集//注意:两个集合必须是有序序列//beg1容器1开始迭代器//end1容器1结束迭代器//beg2容......
  • oracle数据库---PL/SQL、存储函数、存储过程、触发器、定时器job、备份
    PL/SQL什么是PL/SQLPL/SQL(Procedure Language/SQL)是Oracle对sql语言的过程化扩展,指在SQL命令语言中增加了过程处理语句(如分支、循环等),使SQL语言具有过程处理能力。把SQL语言的数据操纵能力与过程语言的数据处理能力结合起来,使得PLSQL面向过程但比过程语言简单......
  • 10.25Python_pandas_函数(1)
    二、函数1、常用的统计学函数函数名称描述说明count()统计某个非空值的数量sum()求和mean()求均值median()求中位数std()求标准差min()求最小值max()求最大值abs()求绝对值prod()求所有数值的乘积案例:#创建一个示例DataFramedata={'A':[1,2,3,4,5],......
  • C语言的 main 函数具体作用是什么
    C语言的mAIn函数具体作用有:1.程序的起点和入口;2.程序的执行流程;3.接收命令行参数;4.程序的结束点;5.操作系统与程序的接口;6.提供程序的整体结构。main函数是C程序的起点和入口。当程序开始执行时,操作系统会首先寻找并调用main函数。1.程序的起点和入口main函数是C程序......
  • exec()系列函数
    exec()系列函数用于在当前进程中执行新的程序,它们是Unix/Linux系统中实现进程控制的重要组成部分。这些函数可以替换当前进程的映像,使其执行不同的程序,而不是创建新的进程。下面介绍主要的exec()系列函数:1.execl用法:接受程序路径、参数列表,参数以NULL结束。示例:execl......
  • 【Python中的内置函数】max、map、zip等函数的实用技巧!
    Python中的内置函数:max、map、zip等函数的实用技巧Python提供了丰富的内置函数,帮助开发者高效编写简洁的代码。在这篇文章中,我们将详细探讨几个常用的内置函数,如max、map和zip,并展示如何在实际项目中灵活运用这些函数。本篇将结合代码示例,深入探讨它们的使用技巧,帮助你......