C语言(递归)

                        Hi~!这里是奋斗的小羊,很荣幸各位能阅读我的文章,诚请评论指点,关注+收藏,欢迎欢迎~~     

                        💥个人主页:小羊在奋斗

                        💥所属专栏:C语言   

        本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为同样是初学者的学友展示一些我的学习过程及心得。文笔、排版拙劣,望见谅。

                               一、递归

                                        1、什么是递归

                                        2、递归的限制条件

                                        3、递归的举例

                                        4、递归与迭代

1、什么是递归

        递归是C语言学习绕不开的一个话题,那什么是递归呢?递归其实是一种解决问题的方法,在C语言中,递归就是函数自己调用自己。若一个复杂的问题,能层层分解为若干个相对简单且与原问题相似的子问题,则原问题可以采用递归算法求解,所以递归的思想就是把大事化小的过程。

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

        来看一个简单的递归示例:

        但是上述代码会陷入死递归,因为递归需要一个限制条件, 不然会一直向内存申请空间但不释放,导致栈溢出(Stack overflow)VS调试技巧中也提到过栈溢出,但这里也不细说,因为内容太多,后面会有专门的文章。

2、递归的限制条件

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

        (1)递归存在限制条件,也就是出口,当满足这个限制条件的时候,递归不再继续;

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

        这么说可能有些生硬,不过在看完了这篇文章后相信你会对这两句话有所体会。

3、递归的举例

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

        我们知道,0和1的阶乘为1,n的阶乘是所有小于及等于n的正整数的积。

        比如:4!= 4*3*2*1,  3!= 3*2*1, 那么4!= 4*3!,即n!= n(n - 1)!。这样的思路就是把一个较大的问题,转换为一个与原问题相似,但规模较小的问题来解决。当n一直减到n = 1的时候,n的阶乘为1,这是我们本来就知道的。到这里,我们就得到了求n的阶乘的公式:

        函数实现如下:

#include <stdio.h>int fact(int x)
{if (0 == x || 1 == x){return 1;}else{return x * fact(x - 1);}
}int main()
{int n = 0;scanf("%d", &n);int m = fact(n);printf("%d\n", m);return 0;
}

         运行结果(这里不考虑n太大的情况,会溢出):

        图形演示:

         3.2输入一个整数顺序打印每一位

        我们拿到这个题目后,很容易想到把这个数的每一位单独拿出来再打印,但问题是,怎么拿到一个数的每一位呢?有一个熟知的方法就是通过模10(%10)和除10(/10)不断循环来取得一个数的每一位,但是这个办法取出来的是逆序的,我们这里需要的是顺序的,很明显我们常用的这个方法行不通。

        虽然这个方法行不通,但是它给了我们一个灵感,我们发现一个数的最低位是很容易得到的,因此我们可以这样想:如果想要按顺序打印1234的每一位,我们可以先打印123,再打印4;打印123之前先打印12,再打印3;打印12之前先打印1,再打印2。这样不就实现我们想要的效果了嘛。

        不难发现,上面解决问题的思路用递归很容易解决,既然有了思路,废话不多说,我们直接上代码演示:

#include <stdio.h>void print(int m)
{if (m > 9){print(m / 10);}printf("%d ", m % 10);
}int main()
{int n = 0;scanf("%d", &n);print(n);return 0;
}

        是不是看起来很简单,运行结果为:

        如果不太明白执行逻辑,不要慌,我直接上图细细演化每一步的执行过程:

4、递归与迭代

         递归是一种很好的编程技巧,但是和很多技巧一样,也是可能会被误用的。就像3.1一样,看到推导的公式,很容易就被写成递归的形式:

        Fact()函数是可以得出正确的结果,但是在函数递归的时候存在一些运行时的开销。

        在C语言中每一次函数调用,都需要为本次函数调用在栈区申请一块内存空间来保存函数调用期间的各种局部变量的值,这块空间被称为运行时栈帧,或者函数栈帧。只要函数不返回,函数对用的栈帧空间就一直被占用,所以如果函数调用中存在递归调用的话,每一次递归函数调用都会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。

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

        为了避免这些问题,就得想其他的办法,通常就是用迭代的方法来代替递归。我们学过的循环就是一种迭代。比如上面的计算n的阶乘问题,我们也可以用循环的方法来解决:

        很明显,虽然递归的思想更容易理解,但是循环的方法更加简洁,也更加高效。

        事实上,我们看到的很多问题都是以递归的形式进行解释的,这只是因为它比非递归的形式更加清晰,但是这些问题的迭代实现往往比递归实现效率高。当一个问题非常复杂,难以使用迭代的方式实现时,递归实现的简洁性就弥补了它所带来时的运行时开销。

        例如:求第n个斐波那契数

        这个问题是不适合使用递归来求解的,但是斐波那契数的问题通常是用递归的形式来解释的:

        看到这个公式,我们很容易写出递归函数:

 

        看似我们用递归很容易的就解决了这个问题,但当我们输入较大的数时,程序运行的过程就会很费劲,比如我们输入50:

 

        可以看到程序运行了半天还没得出结果。为什么呢?其实,我们上面写出的代码在输入较大的数时,会有大量的重复计算,代码执行的效率非常低。这个时候我们就应该换个办法解决,比如循环。 

        那我们用循环具体该怎么实现呢?想要求第n个斐波那契数,无非就是这第n个数的前两个数相加,上面递归的方法其实是逆着计算的,在循环中我们可以顺着计算。假设第一个和第二个数分别为a和b,让a和b相加,值赋给c,再将b的值赋给a,将c的值赋给b,循环执行,直到n的值不再大于2,退出循环。

        当我们求第50个斐波那契数时,也会很快的得出答案(只是结果是错的,因为超过了整型的范围,影响不大):

 

        所以说,递归虽好,但不宜迷恋,要根据实际问题选择合适的解决方法。 

                                       点击跳转主页—> 💥个人主页小羊在奋斗

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA访问不到静态资源

背景 我在resources下创建static文件夹&#xff0c;再创建front文件夹放前端资源&#xff0c;里面有index.html&#xff0c;游览器输入localhost:8011/front没反应。&#xff08;resources/static/front/index.html&#xff09; 解决办法 重启idea&#xff0c;清楚idea缓存&am…

鸿蒙内核源码分析(ELF格式篇) | 应用程序入口并不是main

阅读之前的说明 先说明&#xff0c;本篇很长&#xff0c;也很枯燥&#xff0c;若不是绝对的技术偏执狂是看不下去的.将通过一段简单代码去跟踪编译成ELF格式后的内容.看看ELF究竟长了怎样的一副花花肠子&#xff0c;用readelf命令去窥视ELF的全貌&#xff0c;最后用objdump命令…

MySQL日志机制【undo log、redo log、binlog 】

前言 SQL执行流程图文分析&#xff1a;从连接到执行的全貌_一条 sql 执行的全流程?-CSDN博客文章浏览阅读1.1k次&#xff0c;点赞20次&#xff0c;收藏12次。本文探讨 MySQL 执行一条 SQL 查询语句的详细流程&#xff0c;从连接器开始&#xff0c;逐步介绍了查询缓存、解析 S…

十分钟掌握Java集合之List接口

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

qt day 3

优化登录框&#xff0c;点击登录按钮&#xff0c;如果账号和密码匹配&#xff0c;则弹出 信息对话框 给出提示信息“登录成功”&#xff0c;并给出一个 ok 按钮&#xff0c;当用户点击 ok 后&#xff0c;关闭当前界面&#xff0c;跳转到另一个界面&#xff1b;如果账号和密码不…

医疗器械——血氧仪芯片方案

血氧仪方案使用MCU主控芯片结合红光、红外线传感器&#xff0c;以还原血红蛋白&#xff08;Hb&#xff09;、氧合血红蛋白&#xff08;HbO2&#xff09;在红光和近红外光区域的吸收光谱特性为理论依据&#xff0c;进行血氧含量和心率测量&#xff0c;并将所测结果显示在显示屏上…

区块链 | NFT 水印:Review on Watermarking Techniques(一)

&#x1f34d;原文&#xff1a;Review on Watermarking Techniques Aiming Authentication of Digital Image Artistic Works Minted as NFTs into Blockchains 1 应用于 NFT 的水印技术 常见的水印技术类型可以分为&#xff1a; 可见 v i s i b l e \mathsf{visible} visi…

智慧农业可视化,探索未来农业的新天地

在科技日新月异的今天&#xff0c;农业领域也迎来了翻天覆地的变化。不再只是面朝黄土背朝天&#xff0c;现代科技与农业的结合正在逐步改变着我们的耕种方式。 一、智慧农业&#xff0c;未来已来 步入智慧农业展馆&#xff0c;仿佛进入了一个科幻世界。看似复杂的农业数据&am…

探索鸿蒙开发:鸿蒙系统如何引领嵌入式技术革新

嵌入式技术已经成为现代社会不可或缺的一部分。而在这个领域&#xff0c;华为凭借其自主研发的鸿蒙操作系统&#xff0c;正悄然引领着一场技术革新的浪潮。本文将探讨鸿蒙开发的特点、优势以及其对嵌入式技术发展的深远影响。 鸿蒙操作系统的特点 鸿蒙&#xff0c;作为华为推…

【练习3】

1.将二叉搜索树转为排序的双向链表 (好久没看数据结构&#xff0c;忘完了&#xff0c;学习大佬的代码&#xff09; class Solution { public:Node* prenullptr,*headnullptr; //pre为每次遍历时的前一个节点&#xff0c;head记录头节点Node* treeToDoublyList(Node* root) {if…

2024041702-计算机操作系统 - 死锁

计算机操作系统 - 死锁 计算机操作系统 - 死锁 必要条件处理方法鸵鸟策略死锁检测与死锁恢复 1. 每种类型一个资源的死锁检测2. 每种类型多个资源的死锁检测3. 死锁恢复 死锁预防 1. 破坏互斥条件2. 破坏占有和等待条件3. 破坏不可抢占条件4. 破坏环路等待 死锁避免 1. 安全状态…

Linux进程——Linux进程间切换与命令行参数

前言&#xff1a;在上一篇了解完进程状态后&#xff0c;我们简单了解了进程优先级&#xff0c;然后遗留了一点内容&#xff0c;本篇我们就来研究进程间的切换&#xff0c;来理解上篇提到的并发。如果对进程优先级还有没理解的地方可以先阅读&#xff1a; Linux进程优先级 本篇…