你知道如何使用C语言实现递归吗?

本篇博客会讲解如何使用C语言实现递归,以及2个注意事项。
在这里插入图片描述

递归是什么

递归,简单来说,就是自己调用自己。C语言中,可以使用函数来实现递归,也就是让一个函数自己调用自己。举一个简单的例子:

请你求斐波那契数量的第n项。所谓斐波那契数列,即前2项是1,从第3项开始,每一项都是前2项的和。也就是:1 1 2 3 5 8 13 21 34 55…

分析一下:假设有一个函数int Fib(int n);,当n <= 2时,函数返回1,否则返回Fib(n-1) + Fib(n-2),这就是自己调用自己的最直观、最简单的例子了。

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

如何实现递归

实现递归,需要有“大事化小小事化了”的思路。举个例子:请使用递归打印一个整数的每一位。假设这个函数是void Print(int n);,当n为1234时,会打印1 2 3 4

打印n是一个“大事”,我们可以把它拆分成“小事”。显然,最后一位是最容易打印的,也就是说,1234中的4是最容易打印的,直接1234%10即可,那问题就转换成了先打印123的每一位,再打印4。而123=1234/10

经过我们的分析,打印n其实就2步:

  1. 打印n/10的每一位。
  2. 打印n%10。

如果你这么想,那你还忽略了一点:如果是一位数,比如3,就不需要第一步了,直接进行第二步。换句话说,只有2位数以上才需要第一步。代码实现如下:

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

当我们调用Print(1234)时,输出结果如下:
在这里插入图片描述

递归的调用逻辑

递归是自己调用自己的。我们还是用“打印一个整数的每一位”为例。假设Print(1234);,会发生什么呢?

首先会调用一次Print函数,Print函数再调用Print函数:
在这里插入图片描述
Print函数再调用Print函数:
在这里插入图片描述
Print函数再调用Print函数:
在这里插入图片描述
总算不用继续调用了。此时打印1,返回到上一层:
在这里插入图片描述
打印2,返回上一层:
在这里插入图片描述
打印3,返回上一层:
在这里插入图片描述
最后打印4,返回。

在这里插入图片描述
以上就是整体调用的逻辑。红色的线表示继续调用新的Print函数,即“递推”;紫色的线代表返回到上一层,即“回归”。“递推”和“回归”合称“递归”。

递归的注意事项

函数调用是需要开辟栈帧的,如果无限制的调用下去,栈空间会被耗干,就出现了“栈溢出”现象。为了防止这种情况,递归时需要注意以下2点:

  1. 需要一个限制条件,当不满足这个限制条件时,递归不再继续。
  2. 不断接近这个限制条件。

还是“打印一个整数的每一位”这个例子。可以发现,当满足n>=10时,才会递归调用Print(n/10),这就是递归的限制条件。如果没有这个条件,就会无限递归下去,出现“栈溢出”的错误。

由于每次递归调用都是Print(n/10)n/10之后会接近n>=10这个条件,直到n变成一位数,不满足这个限制条件,递归结束。

注意,以上的2个注意事项只是递归的“必要条件”,而不是“充要条件”。也就是说,满足这2个注意事项,你写的递归函数不一定对,但不满足这2个注意事项的话,你写的递归函数一定是错误的。

递归的优缺点

优点:

  1. 递归可以用少量的代码实现大量重复的计算,代码较为简洁。
  2. 有一些场景非常适合使用递归实现,比如数据结构中的树形结构,基本上都要使用递归来实现各种功能。

缺点:

  1. 当递归的深度太深的时候,会出现“栈溢出”的错误。
  2. 某些场景下,递归的重复计算太多,会导致时间复杂度也偏高。

这里强调一下最后一点,即“递归会增加时间复杂度”。以“求斐波那契数列的第n项”为例,也就是本篇博客开头举的例子,大家可以在自己电脑上测一下,当n是50多的时候,算的会非常慢。这是因为,递归调用时,会有大量重复的计算,整体是一个类似二叉树的结构,时间复杂度为O(2N)。比如计算Fib(50):

  1. Fib(50)会调用Fib(49)和Fib(48)。
  2. Fib(49)调用Fib(48)和Fib(47),Fib(48)调用Fib(47)和Fib(46)。
  3. Fib(48)调用Fib(47)和Fib(46),Fib(47)调用Fib(46)和Fib(45),Fib(47)调用Fib(46)和Fib(45),Fib(46)调用Fib(45)和Fib(44)……

这要是一直调用下去,耗时将以“指数爆炸”的形式增长,程序的时间复杂度太高,效率太低。

所以,递归并不适用于所有情况,一定要在不太影响效率的前提下使用。

总结

  1. 递归就是自己调用自己,实现思路是大事化小小事化了。
  2. 递归是“递推”和“回归”的合称,会先向深处递推,再回归。
  3. 递归需要注意,必须在满足某个限制条件时才继续递归,且每次递归要更加接近这个限制条件。
  4. 递归深度太深,会导致栈溢出。
  5. 递归有可能增加时间复杂度,导致程序效率太低。只有在不过多影响效率的前提下,才推荐使用递归。

感谢大家的阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/16056.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

考验大家指针功底的时候到了:请问如何理解 (int*)1 + 1 ?

来&#xff0c;猜猜看&#xff0c;这里的执行结果是什么&#xff1f; 这是今天课上的一道理解题&#xff0c;给大家一点点思考时间。 &#xff08;心里有答案了再往下滑哦&#xff09; 5 4 3 2 1 . 答案是&#xff0c;报warning&#xff01;因为%d不是用来输出指针的哈…

PromQL,让你轻松实现监控可视化!快来了解一下吧!

Prometheus 中的一些关键设计&#xff0c;比如注重标准和生态、监控目标动态发现机制、PromQL等。 PromQL 是 Prometheus 的查询语言&#xff0c;使用灵活方便&#xff0c;但很多人不知道如何更好利用它&#xff0c;发挥不出优势。 PromQL主要用于时序数据的查询和二次计算场…

学习系统编程No.23【信号实战】

引言&#xff1a; 北京时间&#xff1a;2023/4/23&#xff0c;最近学习状态不怎么好&#xff0c;总是犯困&#xff0c;没精力的感觉&#xff0c;可能是病没有好彻底的原因&#xff0c;也可能是我内心因为生病而认为摆烂理所应当&#xff0c;反正最后导致摆烂&#xff0c;课现在…

Postman预请求脚本、测试脚本(pre-request scripts、tests常用工作总结)

文章目录 Postman预请求脚本&#xff08;pre-request scripts工作常用总结&#xff09;Postman预请求脚本Postman测试脚本预请求脚本和测试脚本有什么区别常用工作总结登录接口返回的是Set-Cookie标头 Postman预请求脚本&#xff08;pre-request scripts工作常用总结&#xff0…

2008-2019年主要城市PITI指数

2008-2019年主要城市PITI指数 1、来源&#xff1a;附在文件内 2、时间区间&#xff1a;2008-2019年 3、具体时间分布&#xff1a;、2008、2009-2010、2011、2012、2013-2014、2014-2015、2015-2016、2016-2017、2017-2018、2018-2019、 4、范围&#xff1a;包括110个城市&a…

Afkayas.1(★)

软件运行 要输入正确的Name和Serial 查壳 一个VB程序&#xff0c;没有加壳 载入OD 直接开搜索字符串。 这里看到了错误的提示&#xff0c;“You Get It”应该就是成功的字符串了。 前面的“AKA-”应该是在什么时候拼接的字符串 去成功的字符串附近看看 这个字符串上面…

网络编程 总结三

一、并发服务器模型 【1】 循环服务器 1>一次只能处理一个客户端的请求&#xff0c;等待这个客户端退出后&#xff0c;才能处理下一个客户端 2>缺点&#xff1a;循环服务器所处理的客户端不能有耗时操作 //*****模型****** sfd socket(); bind(); listen(); while(1)…

js 操作数组内容

js 操作数组内容 数组添加元素&#xff08;更改原数组&#xff09; push和unshift会返回添加了新元素的数组长度 push从数组最后加入&#xff0c;unshift从数组最前面加入 const arr ["a", "b", "c"]; arr.push("d"); //返回4…

【高危】泛微 e-cology <10.57 存在 SQL注入漏洞(POC)(MPS-ndqt-0im5)

漏洞描述 泛微协同管理应用平台(e-cology)是一套企业大型协同管理平台。 泛微 e-cology 受影响版本存在SQL注入漏洞&#xff0c;未经授权的远程攻击者可通过发送特殊的HTTP请求来获取数据库的敏感信息。 漏洞名称GeoServer 存在 sql 注入漏洞漏洞类型SQL注入发现时间2023/4/…

解密PyTorch动态计算图:打破深度学习束缚的秘密武器

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Linux 用户管理与文件权限

Linux 是一个多用户系统&#xff0c;它允许多个用户同时登陆主机&#xff0c;并为他们分配不同的资源和工作环境进行使用。当然&#xff0c;不同的用户都有文件的私有需求&#xff0c;所以设置不同用户文件的权限管理十分重要。 01 用户与用户组 Linux 中一般将文件访问权限的…

2023有哪些适合学生的蓝牙耳机?盘点四款适合学生的无线蓝牙耳机

随着时代的发展&#xff0c;人们更青睐于能够提升生活品质的产品。蓝牙耳机因为摆脱了线的束缚&#xff0c;使用体验会更好。接下来&#xff0c;我来给大家推荐几款适合学生用的无线蓝牙耳机&#xff0c;有需要的朋友可以当个参考。 一、南卡小音舱Lite2蓝牙耳机 参考价&…

【hello Linux】进程间通信——共享内存

目录 前言&#xff1a; 1. System V共享内存 1. 共享内存的理解 2. 共享内存的使用步骤 3. 共享内存的使用 1. 共享内存的创建 查看共享内存 2. 共享内存的释放 3. 共享内存的挂接 4. 共享内存的去挂接 4. 共享内存的使用示例 1. 两进程挂接与去挂接演示&#xff1a; 2. 两进程…

高性能:负载均衡

目录 什么是负载均衡 负载均衡分类 服务端负载均衡 服务端负载均衡——软硬件分类 服务端负载均衡——OSI模型分类 客户端负载均衡 负载均衡常见算法 七层负载均衡做法 DNS解析 反向代理 什么是负载均衡 将用户请求分摊&#xff08;分流&#xff09; 到不同的服务器上…

中移链控制台对接4A平台功能验证介绍

中移链控制台具备单独的注册登录页面&#xff0c;用户可通过页面注册或者用户管理功能模块进行添加用户&#xff0c;通过个人中心功能模块进行用户信息的修改和密码修改等操作&#xff0c;因业务要求&#xff0c;需要对中移链控制台的用户账号进行集中管理&#xff0c;统一由 4…

什么是分布式任务调度?怎样实现任务调度

通常任务调度的程序是集成在应用中的&#xff0c;比如&#xff1a;优惠卷服务中包括了定时发放优惠卷的的调度程序&#xff0c;结算服务中包括了定期生成报表的任务调度程序&#xff0c;由于采用分布式架构&#xff0c;一个服务往往会部署多个冗余实例来运行我们的业务&#xf…

C S S

目录 1.样式定义方式 1.1行内样式表 1.2内部样式表 1.3外部样式表 2.注解 3.选择器 3.1标签选择器 3.2 id选择器 3.3 类选择器 3.4 派生选择器 3.5 伪类选择器 链接伪类选择器&#xff1a; 位置伪类选择器&#xff1a; ​编辑 目标伪类选择器&#xff1a; 复合选…

Winform从入门到精通(37)——FolderBrowserDialog(史上最全)

文章目录 前言1、Name2、Description3、RootFolder4、SelectedPath5、ShowNewFolderButton前言 当需要获取一个可以通过用户自由选择路径的时候,这时候就需要FolderBrowserDialog控件 1、Name 获取FolderBrowserDialog对象 2、Description 用于指示对话框的描述,如下: …

Windows forfiles命令详解,Windows按时间搜索特定类型的文件。

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 forfiles 一、结果输出格式二、按时间搜索三、搜索指定类型文件四、批量删除文件 forfile…

ATTCK v12版本战术介绍——防御规避(四)

一、引言 在前几期文章中我们介绍了ATT&CK中侦察、资源开发、初始访问、执行、持久化、提权战术理论知识及实战研究、部分防御规避战术&#xff0c;本期我们为大家介绍ATT&CK 14项战术中防御规避战术第19-24种子技术&#xff0c;后续会介绍防御规避其他子技术&#xf…