首页 > 其他分享 >初探C语言|浅谈函数的递归

初探C语言|浅谈函数的递归

时间:2024-12-06 19:58:05浏览次数:7  
标签:调用 浅谈 递归 int C语言 fib 初探 阶乘 迭代

文章目录

欢迎讨论:如有错误或不足,欢迎指正和建议,本人主打“听劝”。当然,如有疑问,也期待你在评论区留言互动。
点赞+关注:如果这篇文章对你有帮助,那就支持一下小编吧~~

1.什么是递归?

  • 程序调用自身的编程技巧称为递归( recursion)。
  • 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的。
  • 一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略。
  • 只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
  • 递归的主要思考方式在于:把大事化小

2.递归的两个必要条件

  • 存在限制条件,当满足这个限定条件的时候,递归停止。
  • 每次递归调用后越来越接近这个限制条件

代码示例

我们来看一个简单的递归代码

编写函数不允许创建临时变量,求字符串的长度。

#incude <stdio.h>
int Strlen(const char*str)
{
 if(*str == '\0')
 return 0;
 else
        return 1+Strlen(str+1);
}
int main()
{
 char *p = "abcdef";
 int len = Strlen(p);
 printf("%d\n", len);
 return 0;
}

这里的限制条件就是

str == ‘\0’

递归式为

Strlen(str+1)

不难发现,函数每次调用都会慢慢接近这个条件。

3.两个例题(阶乘和斐波那契)

求n的阶乘。(不考虑溢出)

#include<stdio.h>

int factorial(int x)
{
	if (x <= 1)
		return 1;
	else
		return x * factorial(x - 1);
}
int main()
{
	int n = 0;
	int m = 0;//n的阶乘的值
	scanf("%d", &n);
	m = factorial(n);
	printf("%d", m);
}

求第n个斐波那契数。(不考虑溢出)

#include<stdio.h>

int fib(int x)
{
	if (x <= 2)
		return 1;
	else
		return fib(x - 2) + fib(x - 3);
}

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

发现问题

仔细思考一下这两个代码,你发现了什么?

没错,这两个代码都会出现同样的问题——当n过大时,就会出错。

  • 在使用 fib 这个函数的时候如果我们要计算第50个斐波那契数字的时候特别耗费时间。
  • 使用 factorial 函数求10000的阶乘(不考虑结果的正确性),程序会崩溃。

为什么呢?

我们发现 fib 函数在调用的过程中很多计算其实在一直重复。

我们来看看fib(3)被计算过多少次

int count = 0;//全局变量
int fib(int n)
{
 if(n == 3)
 count++;
 if (n <= 2)         
 return 1;
    else
    return fib(n - 1) + fib(n - 2);
}

最后我们输出看看count,是一个很大很大的值。

那我们如何改进呢?

stack overflow(栈溢出)

  • 在调试 factorial 函数的时候,如果你的参数比较大,那就会报错: stack overflow(栈溢出)这样的信息。
  • 系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出

那如何解决上述的问题:

  1. 将递归改写成非递归。
  2. 使用static对象替代 nonstatic 局部对象。在递归函数设计中,可以使用 static 对象替代nonstatic 局部对象(即栈对象),这不仅可以减少每次递归调用和返回时产生和释放 nonstatic 对象的开销,而且 static 对象还可以保存递归调用的中间状态,并且可为各个调用层所访问。

常规写法(迭代)

比如,下面代码就采用了,非递归的方式(迭代)来实现:

//求n的阶乘
#include<stdio.h>

int main()
{
	int n = 0, m = 1;//m为n阶乘的值。注意初始化值必须为1,因为是累乘。
	int i = 0;
	scanf("%d", &n);
	for (i = 2; i <= n; i++) {
		m *= i;
	}
	printf("%d", m);
}


//求第n个斐波那契数
#include<stdio.h>

int main()
{
	int n = 0;
	scanf("%d", &n);
	int a = 1;
	int b = 1;
	int c = 0;
	if (n < 3)
		n = 1;
	else {
		while (n > 2)
		{
			c = a + b;
			a = b;
			b = c;
			n--;
		}
		n = c;
	}
	printf("%d", n);
}

4. 递归与迭代相比较

  1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
  2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
  3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。

标签:调用,浅谈,递归,int,C语言,fib,初探,阶乘,迭代
From: https://blog.csdn.net/Surplus886/article/details/144299042

相关文章

  • (王道练习代码仓库)408考研真题2022 年42题————C语言
    题目:代码实现:#include<stdio.h>#include<stdlib.h>#include<time.h>#include<string.h>typedefintElemType;typedefstruct{ ElemType*elem; intTableLen;}SSTable;voidST_Init(SSTable&ST,intlen)//申请空间,并进行随机数生成{ ST.Ta......
  • 学习C语言升级c++的笔记
    此篇文章在2022年2月8日被记录,工作这两年多了,也没用过C++做开发,令人唏嘘1、#include<cmath>#include<cstdio>用这种方法来调用C语言中的函数2、namespace名字空间,防止命名重复::叫做限定调用符usingnamespaceX:引入整个名字空间usingX::name使用单个名字X::na......
  • c语言题目 之 杨氏矩阵
    杨氏矩阵,就是行和列里面的值都是越来越大的;第一种:通过结构体将坐标带回第二种:通过指针将坐标带回//方法一:structpoint{ intx; inty;};structpointserch_(int(*p)[4],intr,intc,intn){ intx=0; inty=c-1; structpointrety={-1,-1}; ......
  • C语言实验 二维数组
    时间:2024.12.6一、实验7-1矩阵运算代码 #include<stdio.h>intmain(){inta[20][20]={0};intn,i,j;intsum=0;scanf("%d",&n);for(i=0;i<n;i++){for(j=0;j<n;j++){scanf("%d",&a[i][j])......
  • C语言:assert断言(如何让程序在不满足条件时报错)
    目录简介如何使用简介assert()是包含在assert.h头文件的宏,用于在运行时确保程序符合指定的条件,如果不符合条件,就报错并终止运行。这个宏被称为“断言”例子:assert(a>b);这个代码的作用就是,如果程序运行到该行代码时,不满足a>b这个条件的话,程序便会报错并停......
  • OPCUA 探讨(二)——服务器节点初探
    一、回顾前文中我们获取到了一份现成的OPCUA客户端代码,通过该客户端和ProsysOPCUA服务器建立了连接,并浏览了其中服务器上的内容(多层的树状节点结构)。OPCUA探讨(一)二、服务器节点结构以下是对OPCUA服务器节点结构的简要讨论。2.1根目录结构前文中我们建立会话连接后,首次点......
  • 浅谈APS生产排程系统产线级日计划应用
    在制造型企业纠结先上APS计划排程系统还是MES生产管理系统时,APS厂商已经打通了ERP到MES执行的最后一公里,我们称供应链计划和生产计划。车间计划的执行涉及到仓储备料、资源调度、派工作业、工序汇报、生产入库等,而日计划的执行效益反应一个工厂综合能力。订单及时交付率是制造型......
  • 【C语言】在 Linux 终端编写、编译并运行 Hello world 程序
    步骤创建并打开hello-world文件夹mkdirhello-worldcdhello-world使用vim创建main.cvimmain.c写入代码并保存#include<stdio.h>intmain(){printf("Hello,world!\n");return0;}#include<stdio.h>是一个预处理命令,用于包含标准输入输......
  • ETL是什么?浅谈ETL对数据仓库的重要性
    在当今数字化浪潮席卷全球的时代,存在着大量的数据孤岛,企业对于数据的重视程度达到了前所未有的高度。有效集成数据也成为企业决策分析过程的重中之重,ETL对数据集成发挥着至关重要的作用。那么,什么是ETL?为何ETL如此重要?企业决策又该如何应用ETL?下文为您一一揭晓。什么是ETL?ETL,即......
  • c语言基础一:第一个c语言程序
    一、编写代码#include<stdio.h>intmain(){//这是第一个c语言代码printf("helloworld");return0;}c语言源代码文件可以是任意的一个普通文件文本,但扩展名必须是.c二、通过gcc编译c代码1.gcc编译器介绍编辑器是指我们用来写代码的程序,而在编辑器中写的......