C语言函数递归(含扫雷进阶思路)

news2024/11/16 12:54:00

文章目录

  • 一、什么是递归
  • 二、递归的使用思路和限制条件
    • 1.递归的使用思路
    • 2.递归的限制条件
  • 三、递归的举例
    • 举例1:求n的阶乘
    • 2.举例2:顺序打印⼀个整数的每⼀位
  • 四、递归与迭代对比
  • 五、递归与迭代对比举例
  • 七、扫雷进阶思路

一、什么是递归

    递归是学习C语⾔函数绕不开的⼀个话题,那什么是递归呢?
    递归其实是⼀种解决问题的⽅法,在C语⾔中,递归就是函数⾃⼰调⽤⾃⼰。
写⼀个史上最简单的C语⾔递归代码:
在这里插入图片描述
    上述就是⼀个简单的递归程序,只不过上⾯的递归只是为了演⽰递归的基本形式,不是为了解决问题,代码最终也会陷⼊死递归,导致栈溢出,因为代码每执行完printf时,又调用了main函数,也就是又从main函数的头开始,然后再打印,最后一陷入死递归,如果代码突然结束,可能就是程序一直在创建函数栈帧,导致了栈溢出

二、递归的使用思路和限制条件

1.递归的使用思路

    把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但规模较⼩的⼦问题来求解;直到⼦问题不能再被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化⼩的过程
    递归中的递就是递推的意思,归就是回归的意思,现在不懂没关系,接下来慢慢来体会

2.递归的限制条件

递归在书写的时候,有2个必要条件:
    • 递归存在限制条件,当满⾜这个限制条件的时候,递归便不再继续,如果没有限制,可能会陷入死递归
    • 每次递归调⽤之后越来越接近这个限制条件。
在下⾯的例⼦中,我们逐步体会这2个限制条件

三、递归的举例

举例1:求n的阶乘

    ⼀个正整数的阶乘(factorial)是所有⼩于及等于该数的正整数的积,并且0的阶乘为1。
⾃然数n的阶乘写作n!
(1)我们知道n的阶乘的公式: n! = n ∗ (n − 1)!
如:

      5! = 5*4*3*2*1
      4! = 4*3*2*1
   //所以:5! = 5*4!

    这样的思路就是把⼀个较⼤的问题,转换为⼀个与原问题相似,但规模较⼩的问题来求解的
    当 n==0 的时候,n的阶乘是1,其余n的阶乘都是可以通过公式计算
(2)n的阶乘的递归公式如下:
在这里插入图片描述
    所以我们可以在函数fact中调用fact函数,实现递推,每次递推n都减1,直到n等于0,随后函数开始返回,最后算出n的阶乘,如:
在这里插入图片描述
运行结果:
在这里插入图片描述
(3)画图整个过程演示:
在这里插入图片描述

2.举例2:顺序打印⼀个整数的每⼀位

    输⼊⼀个整数m,按照顺序打印整数的每⼀位
比如:

输⼊:1234 输出:1 2 3 4
输⼊:520  输出:5 2 0

(1)分析:
    这个题⽬,放在我们⾯前,⾸先想到的是,怎么得到这个数的每⼀位呢?
    如果n是⼀位数,n的每⼀位就是n⾃⼰,n是超过1位数的话,就得拆分每⼀位
    拆分的方法之前也讲到过,就是%10可以得到最后一位,/10可以去掉最后一位,但是我们要按照顺序打印,一个思路就是直接按上面的方法处理,然后再将它倒着打印即可,我们接下来将的是使用递归的思路
    想要用递归解决这个问题,那么我们就要明白使用递归的方法思路,也就是将一个大的问题逐步的化解为一个又一个的小问题,先递推,然后到了某种条件再回归,最后帮我们实现任务
    比如我们现在有一个函数叫print,它的作用就是帮我们将一个整数的每一位给打印出来,假如打印1234的每一位,那么就可以拆分成print(123) + print(4),然后print(123)又可以拆分为print(12) + print(3),以此类推,如下:

 Print(1234)
==>Print(123) + printf(4)
==>Print(12) + printf(3)
==>Print(1) + printf(2)
==>printf(1) 

    那么递归如何结束呢?我们可以思考,什么情况下函数无需再次递归,没错,就是当这个数只剩下一位数的时候,我们就可以直接将它打印出来,无需递归,那么怎么判断这个数是不是一位数呢?我们就可以将9这个界限找出来,如果一个整数大于9那么它肯定不是一位数,反之它就是个一位数,现在限制条件也清楚了,这个代码也就迎刃而解了

(2)代码实现以及运行结果:
在这里插入图片描述
在这里插入图片描述
    在这个解题的过程中,我们就是使⽤了⼤事化⼩的思路,把print(1234) 打印1234每⼀位,拆解为⾸先print(123)打印123的每⼀位,再打印得到的4
    把print(123) 打印123每⼀位,拆解为⾸先print(12)打印12的每⼀位,再打印得到的3
直到Print打印的是⼀位数,直接打印就⾏

(3)画图演示:
在这里插入图片描述

四、递归与迭代对比

    递归是⼀种很好的编程技巧,但是和很多技巧⼀样,也是可能被误⽤的,就像举例1⼀样,看到推导的公式,很容易就被写成递归的形式:
在这里插入图片描述
    在C语⾔中每⼀次函数调⽤,都需要为本次函数调⽤在内存的栈区,申请⼀块内存空间来保存函数调⽤期间的各种局部变量的值,这块空间被称为运⾏时堆栈,或者函数栈帧
    函数不返回,函数对应的栈帧空间就⼀直占⽤,所以如果函数调⽤中存在递归调⽤的话,每⼀次递归函数调⽤都会开辟属于⾃⼰的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间
    所以如果采⽤函数递归的⽅式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(stack overflow)的问题,关于函数栈帧的创建与销毁的详细过程,会在以后的视频进行讲解
    如果不想使⽤递归,就得想其他的办法,通常就是迭代的⽅式(通常就是循环的⽅式)
⽐如:计算 n 的阶乘,也是可以产⽣1~n的数字累计乘在⼀起的,如图:
在这里插入图片描述
    上述代码是能够完成任务,并且效率是⽐递归的⽅式更好的
    事实上,我们看到的许多问题是以递归的形式进⾏解释的,这只是因为它⽐⾮递归的形式更加清晰,但是这些问题的迭代实现往往⽐递归实现效率更⾼。
    当⼀个问题⾮常复杂,难以使⽤迭代的⽅式实现时,此时递归实现的简洁性便可以补偿它所带来的运⾏时开销。

五、递归与迭代对比举例

需求:求第n个斐波那契数
    计算第n个斐波那契数,是不适合使⽤递归求解的,但是斐波那契数的问题通过是使⽤递归的形式描述的,如下:
在这里插入图片描述
    看这个形式,很容易又到我们写出递归,如:
在这里插入图片描述
    当我们输入50时,代码会停住很久,并且这个时间长到我们无法接受,这就是因为函数fib在递归时,创建的函数栈帧太多了,一直递推,一直返回,并且还伴随着多个重复,导致代码卡在那里,如图:
在这里插入图片描述
    其实递归程序会不断的展开,在展开的过程中,我们很容易就能发现,在递归的过程中会有重复计算,⽽且递归层次越深,冗余计算就会越多。我们可以作业测试:
在这里插入图片描述
运行结果为:
在这里插入图片描述
    算出的fib(50)的值超过了int的上限,所以出错了,但是后面count的计数没有问题,可以看到光是算个fib(50)居然就算了5亿多次fib(3),可见这个代码有多浪费空间,多没有效率,所以这种情况我们可以使用迭代替换,我们知道斐波那契数的前2个数都是1,然后前2个数相加就是第3个数,那么我们从前往后,从⼩到⼤计算就⾏了,如图:
在这里插入图片描述
    如果我们再次输入50让它计算,可以看到几乎瞬间就可以得到答案,虽然答案还是会因为超出int最大值而错误,但是至少我们知道这样运行效率很高

六、 递归拓展学习

  1. ⻘蛙跳台阶问题
  2. 汉诺塔问题

可以尝试自己解决,解析和答案在下期给出,敬请期待!

七、扫雷进阶思路

    在上一篇带着大家写了扫雷基本架构,算是简单实现了扫雷的玩法,但是还是有很多缺陷,比如不能标记,在排查坐标周围没有雷,不能扩展的等等问题
    但是实际上现在我们已经有能力实现这些需求,比如标记,我们可以在用户排完坐标后进行询问是否标记雷,然后用某个符号代替标志,比如排查坐标周围没有雷时,可以进行扩展,这不就跟我们今天学习的递归紧密相连吗?将扩展一片没有雷的区域,化小为某个坐标扩展加上其它坐标扩展,反复递推,然后回归,我们学的递归就很有用了
    现在我们学习了递归,在这里我给出思路,希望友友们可以通过自己的思考将扫雷篇章的那些扩展写出来,当然,我会在下一篇总体出一个扫雷进阶的实现,敬请期待吧!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

暄桐教室分享“闲人”指南

一种理想的生活状态,叫“做个闲人”,如苏东坡《行香子述怀》那般,“对一张琴,一壶酒,一溪云”,放下纷扰,好自在。然而,闲并不是简单的无事可做,让自己时光充沛、能量聚集…

MacOS使用FileZilla通过ssh密钥文件连接远程服务器(已解决)

需求描述 mac电脑,使用filezilla通过FTP连接远程服务器,使用ssh密钥文件代替密码。 版本信息 MacOS:Sonoma 14.5 M3芯片 FileZilla:3.66.5 在这里插入图片描述 连接 1. 创建站点 打开filezilla工具,右上角选择“文件 -> 站点管理器”,打开站点管理器弹窗。 2.…

vue 动态替换父组件

替换父组件?? 什么鬼??? 这个场景的确很少见!!不过我们要说的的确是要替换父组件!!!!!! 就是子组件内容不变但是父组件变…

【HuggingFace Transformers】LlamaDecoderLayer源码解析

LlamaDecoderLayer源码解析 1. LlamaDecoderLayer 介绍2. LlamaDecoderLayer 类源码解析 1. LlamaDecoderLayer 介绍 LlamaDecoderLayer 是 LLaMA 模型中的一个关键组件,它结合了自注意力机制、全连接层和残差连接,以及对输入数据的归一化。主要流程为&…

SpringCloud之一IDEA导入已有微服务项目并启动服务

一|、导入已有微服务项目 启动idea,file --> open,选择项目根目录;点击屏幕右侧maven projects按钮 -->点那个绿,如果屏幕右侧没有maven projects按钮,点击Help->Find Action,输入maven&#xff…

算法-存在重复元素(219)

这道题一眼看过去暴力,两层循环,找到相等的数字,然后判断一下就行,但是这样的话不符合哈希表使用原则。这道题同样利用了hash表键值配对的规则。 class Solution {public boolean containsNearbyDuplicate(int[] nums, int k) {M…

C语言 | Leetcode C语言题解之第382题链表随机节点

题目: 题解: typedef struct {struct ListNode * head; } Solution;Solution* solutionCreate(struct ListNode* head) {Solution * obj (Solution *)malloc(sizeof(Solution));assert(obj ! NULL);obj->head head;return obj; }int solutionGetRa…

keil中内存的存储规律

keil中内存的存储规律 keil中内存的存储规律 文章目录 keil中内存的存储规律keil中内存的存储规律 keil中内存的存储规律 #include <stdlib.h> #include "gd32f30x.h" #include "led_drv.h" #include "delay.h" #include "key_drv.…

GIT 下载安装使用教程

一. GIT下载 git下载地址https://git-scm.com/downloads 二. git安装 1. 许可声明 看完许可声明&#xff0c;点击Next就好了 2. 选择安装路径 默认为C盘&#xff0c;可以修改&#xff0c;这里修改为D盘&#xff0c;点击Next 3. 组件选择 勾选添加在桌面上&#xff0c;就是…

android gradle特别慢

gradle下载慢 修改gradle-wrapper.properties 替换https://services.gradle.org/distributions为https://mirrors.cloud.tencent.com/gradle distributionBaseGRADLE_USER_HOME distributionPathwrapper/dists distributionUrlhttps\://mirrors.cloud.tencent.com/gradle/g…

jenkins发布文件到远程服务器

jenkins安装 安装教程 后台启动脚本 创建脚本&#xff1a;start_jenkins.sh ls for pid in $(ps -ef|grep jenkins.war|grep -v grep|cut -c 10-16); doecho $pid;kill -9 $pid; done;nohup java -Djava.awt.headlesstrue -jar /usr/local/jenkins/jenkins.war --webroot/…

Linux入门攻坚——30、sudo、vsftpd

su&#xff1a;Switch User&#xff0c;即切换用户 su [-l user] -c ‘COMMAND’ 如&#xff1a;su -l root -c ‘COMMAND’ 如果没有指定-l user&#xff0c;则默认是root sudo&#xff1a;可以让某个用户不需要拥有管理员的密码&#xff0c;而可以执行管理员的权限。 需…

RabbitMQ练习(Topics)

1、RabbitMQ教程 《RabbitMQ Tutorials》https://www.rabbitmq.com/tutorials 2、环境准备 参考&#xff1a;《RabbitMQ练习&#xff08;Hello World&#xff09;》和《RabbitMQ练习&#xff08;Work Queues&#xff09;》。 确保RabbitMQ、Sender、Receiver、Receiver2容器…

云原生向量数据库 PieCloudVector 助力多模态大模型 AI 应用

全球 AGI&#xff08;人工通用智能&#xff09;市场快速增长的背景下&#xff0c;企业应用成为推动这一领域发展的主要力量&#xff0c;企业如何选择合适的技术来支撑其智能化转型显得尤为重要。在墨天轮《数据库技术如何增强 AI 大模型&#xff1f;》数据库沙龙活动中&#xf…

C语言典型例题55

《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 题目&#xff1a; 例题4.7 兔子的繁殖。这是一个有趣的古典问题&#xff1a;有一对兔子&#xff0c;从出生后的第3个月开始起每个月都生一对兔子。小兔子长到第3个月又生一对兔子。假设所有兔子都不死&#xff0c;…

深度解读SGM41511电源管理芯片I2C通讯协议REG08寄存器解释

REG08 是 SGM41511 的第九个寄存器&#xff0c;地址为 0x08。这是一个只读&#xff08;R&#xff09;寄存器&#xff0c;用于报告各种状态信息。上电复位值&#xff08;PORV&#xff09;为 xxxxxxxx&#xff0c;表示上电时的初始状态是不确定的。这个寄存器提供了充电器当前状态…

HarmonyOS--合理使用页面间转场

一、概述 页面间转场是用户从一个页面切换到另一个页面时的过程&#xff0c;一个无缝流畅的转场动效可以提升用户的交互体验。从主页到详情页、从列表页到结果页都需要去设置一些转场动效使得用户体验更加流畅。基于用户行为和应用设计模式&#xff0c;我们总结出了一些常见的转…

C#/.net core “hello”.IndexOf(“\0”,2)中的坑

先想想看&#xff0c;你认为下面代码返回值是多少&#xff1f; "hello".IndexOf("", 2); "hello".IndexOf("\0", 2); "hello".IndexOf(\0, 2); 今天和大家分享关于.net core中与字符相关的一些奇怪问题。 首先我们先以.N…

Golang | Leetcode Golang题解之第383题赎金信

题目&#xff1a; 题解&#xff1a; func canConstruct(ransomNote, magazine string) bool {if len(ransomNote) > len(magazine) {return false}cnt : [26]int{}for _, ch : range magazine {cnt[ch-a]}for _, ch : range ransomNote {cnt[ch-a]--if cnt[ch-a] < 0 {r…

大模型知识检索RAG业务实践实践(初级篇)

大模型知识检索RAG业务实践实践(初级篇) 1.知识检索大图 大模型是现在一个非常热门的话题,大模型表现出的生成能力也是非常惊艳。但是强如 GPT4 这样的大模型,它在知识更新和幻觉上也会存在问题。比如说我们问互联网行业有什么大事,GPT4 的回答是三年前的内容。主要是说疫…