【C语言系列】函数递归
函数递归
- 一、递归是什么?
- 1.1尾递归
- 二、递归的限制条件
- 三、递归举例
- 3.1举例一:求n的阶乘
- 3.2举例二:顺序打印一个整数的每一位
- 四、递归与迭代
- 4.1举例三:求第n个斐波那契数
- 五、拓展学习
- 青蛙跳台问题
一、递归是什么?
递归其实是一种解决问题的方法,在C语言中,递归就是函数自己调用自己。
下面我们看最简单的递归代码:
#include <stdio.h>
int main()
{
printf("hehe\n");
main();
return 0;
}
运行后提示报错:
这里的代码会导致栈溢出,虽然是个错误的代码但是能够很明确的表达出递归的意思,在这里我们可以看出这个代码一直在执行打印操作,体现了函数递归的现象,但是由于死循环的打印导致了栈溢出的现象。
递归的思想: 把⼀个大型复杂问题层层转化为⼀个与原问题相似,但规模较小的子问题来求解;直到子问题不能再被拆分,递归就结束了。即就是把大事化小的过程。
递归中的递就是递推的意思,归就是回归的意思。
1.1尾递归
尾递归是指一个递归函数在调用自身时,该递归调用是函数的最后一条语句。换句话说,函数在调用自身之后不再执行任何操作,而是直接返回递归调用的结果。这种特殊形式的递归称为尾递归。
二、递归的限制条件
递归在书写的时候,有2个必要条件:
①递归存在限制条件,当满足这个限制条件的时候,递归便不再继续。
②每次递归调用之后越来越接近这个限制条件。
在下面的例子中,我们逐步体会这2个限制条件
三、递归举例
3.1举例一:求n的阶乘
⼀个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。 自然数n的阶乘写作n!。
题目:计算n的阶乘(不考虑溢出),n的阶乘就是1~n的数字累积相乘。
分析和实现示例:
我们知道n的阶乘的公式: n! = n ∗ (n − 1)! 举例说明: 5! = 1 * 2 * 3 * 4 * 5; 4! =1 *
2 * 3 * 4;即 5!就等4!* 5。
当 n==0 的时候,n的阶乘是1,其余n的阶乘都是可以通过公式计算。
如图所示:
实现代码如下:
#include <stdio.h>
int Fact(int n)
{if(n==0)return 1;elsereturn n*Fact(n-1);
}
int main()
{int n = 0;scanf("%d", &n);int ret = Fact(n);printf("%d\n", ret);return 0;
}
运行结果(当然这里不考虑n太大的情况,n太大存在栈溢出的现象)如图:
下面我们画图推演:
3.2举例二:顺序打印一个整数的每一位
题目:输入⼀个整数m,按照顺序打印整数的每⼀位。
比如:
输入:1234 输出:1 2 3 4
输入:520 输出:5 2 0
分析和实现示例: 怎么得到这个数的每⼀位呢?
如果n是⼀位数,n的每⼀位就是n自己。
n是超过1位数的话,就得拆分每⼀位1234%10就能得到4,然后1234/10得到123,这就相当于去掉了4,然后继续对123%10,就得到了3,再除10去掉3,以此类推不断的%10 和 /10 操作,直到得到1234的每一位。
想到这里我们便有了思路:
把Print(1234) 打印1234每⼀位,拆解为首先Print(123)打印123的每⼀位,再打印得到的4。
把Print(123) 打印123每⼀位,拆解为首先Print(12)打印12的每⼀位,再打印得到的3。
直到Print打印的是⼀位数,直接打印就行。
代码实现如下:
void Print(int n)
{if(n>9){Print(n/10);}printf("%d ", n%10);
}
int main()
{int m = 0;scanf("%d", &m);Print(m);return 0;
}
运行结果如图:
画图推演:
四、递归与迭代
递归是⼀种很好的编程技巧,但是和很多技巧⼀样,也是可能被误用的,就像举例1一样,看到推导的公式,很容易就被写成递归的形式:
int Fact(int n)
{if(n==0)return 1;elsereturn n*Fact(n-1);
}
Fact函数是可以产生正确的结果,但是在递归函数调用的过程中涉及⼀些运行时的开销。
在C语言中每⼀次函数调用,都要需要为本次函数调用在栈区申请⼀块内存空间来保存函数调用期间的各种局部变量的值,这块空间被称为运行时堆栈,或者函数栈帧。
函数不返回,函数对应的栈帧空间就⼀直占用,所以如果函数调用中存在递归调用的话,每⼀次递归函数调用都会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。所以如果采用函数递归的方式完成代码,递归层次太深,就会浪费太多的栈帧空间,也可能引起栈溢出(stackoverflow)的问题。
所以如果不想使用递归就得想其他的办法,通常就是迭代的方式(通常就是循环的方式)。
比如:计算n的阶乘,也是可以产生1~n的数字累计乘在⼀起的。
int Fact(int n)
{int i = 0;int ret = 1;for(i=1; i<=n; i++){ret *= i;}return ret;
}
上述代码是能够完成任务,并且效率是比递归的方式更好的。
4.1举例三:求第n个斐波那契数
计算第n个斐波那契数,是不适合使用递归求解的,但是斐波那契
数的问题通过是使用递归的形式描述的,如下:
看到上图我们就会想到用递归的形式去做,如下代码:
#include <stdio.h>
int Fib(int n)
{if(n<=2)return 1;elsereturn Fib(n-1)+Fib(n-2);
}
int main()
{int n = 0;scanf("%d", &n);int ret = Fib(n);printf("%d\n", ret); return 0;}
当我们n输⼊为50的时候,需要很⻓时间才能算出结果,这个计算所花费的时间,是我们很难接受的,这也说明递归的写法是非常低效的,那是为什么呢?
其实递归程序会不断的展开,在展开的过程中,我们很容易就能发现,在递归的过程中会有重复计算,而且递归层次越深,冗余计算就会越多。我们可以一个测试:
#include <stdio.h>
int count = 0;
int Fib(int n)
{if(n == 3)count++;//统计第3个斐波那契数被计算的次数if(n<=2)return 1;elsereturn Fib(n-1)+Fib(n-2);
}
int main()
{int n = 0;scanf("%d", &n);int ret = Fib(n);printf("%d\n", ret); printf("\ncount = %d\n", count);return 0;
}
如图所示,计算第40个斐波那契数时,需要计算 39088169次,可想而知这种方法是冗杂的,那这种问题就不适合用递归的方式来来解决问题,接下来我们使用迭代的方式来解决这个问题。
我们知道斐波那契数的前2个数都1,然后前2个数相加就是第3个数,那么我们从前往后,从小到大计算就行了。
代码如下:
int Fib(int n)
{int a = 1;int b = 1;int c = 1;while(n>2){c = a+b;a = b;b = c;n--;}return c;
}
迭代的方式去实现这个代码,效率就要高出很多了。
五、拓展学习
青蛙跳台问题
问题:有一只青蛙,一次可以跳1个台阶,一次也可以跳2个台阶,问:如果跳到第n个台阶,有多少种跳法?
分析与实现问题:
这只青蛙,第一次条有两种跳法:
①跳一个台阶,剩余n-1个台阶
②跳两个台阶,剩余n-2个台阶
不难发现这是一个斐波那契数列,即用以下方法:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Jump(int n)
{if (n <= 2)return n;elsereturn Jump(n - 1) + Jump(n - 2);
}int main()
{int x = 0;scanf("%d", &x);int ret = Jump(x);printf("func(%d)=%d\n", x, ret);return 0;
}
相关文章:
【C语言系列】函数递归
函数递归 一、递归是什么?1.1尾递归 二、递归的限制条件三、递归举例3.1举例一:求n的阶乘3.2举例二:顺序打印一个整数的每一位 四、递归与迭代4.1举例三:求第n个斐波那契数 五、拓展学习青蛙跳台问题 一、递归是什么? …...
windows10 安装 Golang 版本控制工具g与使用
下载包:https://github.com/voidint/g/releases 解压, 并添加到环境变量 g 常用命令 查询当前可供安装的stable状态及所有的 go 版本 # stable 版本 g ls-remote stable# 所有版本 g ls-remote安装目标 go 版本1.23.4g install 1.23.4切换到已安装的…...
WordPress开发进群V2主题,多种引流方法,引私域二次变现
介绍: 全新前端UI界面,多种前端交互特效让页面不再单调,进群页面群成员数,群成员头像名称,每次刷新页面随机更新不重复,最下面评论和点赞也是如此随机刷新不重复 进群页面简介,群聊名称&#…...
在 CentOS/Red Hat Linux 中安装 Docker
在 Red Hat Linux 中安装 Docker 在 Red Hat Linux (RHEL) 中安装 Docker 需要一些准备工作,尤其是针对不同版本的系统(如 RHEL 7、8、9)。以下是具体的安装步骤: 步骤 1:检查系统版本 在安装前,确认系统…...
【DAPM杂谈之二】实践是检验真理的标准
本文主要分析DAPM的设计与实现 内核的版本是:linux-5.15.164,下载链接:Linux内核下载 主要讲解有关于DAPM相关的知识,会给出一些例程并分析内核如何去实现的 /**************************************************************…...
关于使用FastGPT 摸索的QA
近期在通过fastGPT,创建一些基于特定业务场景的、相对复杂的Agent智能体应用。 工作流在AI模型的基础上,可以定义业务逻辑,满足输出对话之外的需求。 在最近3个月来的摸索和实践中,一些基于经验的小问题点(自己也常常…...
虚拟文件系统 VFS
目录 虚拟文件系统 VFS 文件系统挂载过程 虚拟文件系统 VFS 统一标准的系统调用接口: VFS定义了一组标准的文件操作API,如open(), read(), write(), close()等,使得用户空间的应用程序无需关心底层文件系统的具体类型。 下层文件系统必须实现…...
React Fiber框架中的Render渲染阶段——workLoop(performUnitOfWork【beginWork与completeWork】)
触发渲染过程——renderRoot renderRoot 是一个函数,用于触发渲染工作。它通常会调用并递归地执行一系列的渲染任务,直到完成整个更新过程。这个过程包括执行 Fiber 树中的 beginWork 和 completeWork,以及渲染新状态或 DOM。 function ren…...
Xcode 正则表达式实现查找替换
在软件开发过程中,查找和替换文本是一项常见的任务。正则表达式(Regular Expressions)是一种强大的工具,可以帮助我们在复杂的文本中进行精确的匹配和替换。Xcode 作为一款流行的开发工具,提供了对正则表达式的支持。本…...
【opencv】第8章 图像轮廓与图像分割修复
8.1 查找并绘制轮廓 一个轮廓一般对应一系列的点,也就是图像中的一条曲线。其表示方法可能 根据不同的情况而有所不同。在OpenCV 中,可以用findContours()函数从二值图 像中查找轮廓 8.1.1 寻找轮廓: findContours() 函数 findContours) 函…...
excel VBA 基础教程
这里写目录标题 快捷键选择所有有内容的地方 调试VBA录制宏,打开VBA开发工具录制宏,相当于excel自动写代码(两个表格内容完全一致才可以) 查看宏代码保持含有宏程序的文件xlsm后缀(注意很容易有病毒)宏文件安全设置 使…...
2008-2019年各省城镇人口数据
2008-2019年各省城镇人口数据 1、时间:2008-2019年 2、来源:国家统计局、统计年鉴 3、指标:行政区划代码、地区、年份、城镇人口 4、范围:31省 5、指标解释:城镇人口是指居住在城镇范围内的全部常住人口。 6、下…...
【机器学习】在不确定的光影中:机器学习与概率论的心灵共舞
文章目录 概率与统计基础:解锁机器学习的数据洞察之门前言一、概率论基础1.1 概率的基本概念与性质1.1.1 概率的定义1.1.2 样本空间与事件1.1.3 互斥事件与独立事件1.1.4 概率的计算方法 1.2 条件概率与独立性1.2.1 条件概率1.2.2 独立事件 1.3 随机变量1.3.1 随机变…...
vscode使用Marscode编程助手
下载 vscode 在插件里下载Marscode编程助手 插件完成 在这里点击安装,点击后这里出现AI编程插件。...
谷歌开放语音命令数据集,助力初学者踏入音频识别领域
在人工智能的浪潮中,语音识别技术正逐渐成为我们日常生活的一部分。从智能助手到语音控制设备,语音识别的应用场景越来越广泛。然而,对于初学者来说,进入这一领域往往面临诸多挑战,尤其是缺乏合适的开源数据集和简单的…...
Diffchecker图像比较工具介绍
Diffchecker图像比较工具介绍 网站地址: Diffchecker图像比较 主要功能: 图像差异比较: 该工具允许用户上传两张图片,系统会自动识别并高亮显示这两张图片之间的差异。简单易用: 用户只需将图片拖放到指定区域或点击浏…...
后端开发 Springboot整合Redis Spring Data Redis 模板
目录 redis 配置 RedisConfig 类 完整代码 代码讲解 1. 类定义和注解 2. 定义 RedisTemplate Bean 3. 配置 JSON 序列化 4. 配置 Redis 的 key 和 value 序列化方式 5. 完成配置并返回 RedisTemplate 总结 redis 服务接口实现类 类级别 注入 RedisTemplate 常用 Re…...
极狐GitLab 正式发布安全版本17.7.1、17.6.3、17.5.5
本分分享极狐GitLab 补丁版本 17.7.1, 17.6.3, 17.5.5 的详细内容。这几个版本包含重要的缺陷和安全修复代码,我们强烈建议所有私有化部署用户应该立即升级到上述的某一个版本。对于极狐GitLab SaaS,技术团队已经进行了升级,无需用户采取任何…...
策略模式详解与应用
策略模式(Strategy Pattern),是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换,而应用程序可以在运行时选择使用哪一个算法。策略模式使得算法的变化独立于使…...
Gateway怎么实现限流的
Gateway怎么实现限流的 在API网关(如Spring Cloud Gateway、Kong、Nginx等)中实现限流是为了控制服务请求的频率,从而避免系统过载,确保稳定性和可用性。限流可以通过多种策略实现,常见的方法包括基于请求次数、时间窗…...
OpenCV实现Kuwahara滤波
Kuwahara滤波是一种非线性的平滑滤波技术,其基本原理在于通过计算图像模板中邻域内的均值和方差,选择图像灰度值较为均匀的区域的均值来替代模板中心像素的灰度值。以下是Kuwahara滤波的详细原理说明: 一、基本思想 Kuwahara滤波的基本思想…...
【DevOps】Jenkins使用Pipeline构建java代码
使用Pipeline发布java项目 文章目录 使用Pipeline发布java项目资源列表基础环境一、准备gitlab1.1、部署gitlab1.2、创建chinanews项目1.3、提交代码1.4、查看上传的代码 二、准备Jenkins2.1、部署Jenkins2.2、安装maven2.3、修改Maven源2.4、准备chinanews 三、Jenkins配置工具…...
【网络云SRE运维开发】2025第2周-每日【2025/01/12】小测-【第12章 rip路由协议】理论和实操考试题
文章目录 选择题理论题 解释RIP协议中的“水平分割”机制,并说明其目的。 可以防止路由器错误地将从邻居学到的路由再发送回给该邻居,从而避免路由环路的发生。实操题 【网络云SRE运维开发】2025第2周-每日【2025/01/12】小测-【第12章 rip路由协议】理论…...
Entity 的材质(棋盘、条纹、网格)
Entity 的材质 普通物体的材质 import { nextTick, onMounted, ref } from vue import * as Cesium from cesium // console.log(Cesium, Cesium)const viewer ref<any>(null)onMounted(() > { ... })let material Cesium.Color.YELLOW.withAlpha(0.5)Cesium.Colo…...
shell脚本编写练习3
1、shell 脚本写出检测 /tmp/size.log 文件如果存在显示它的内容,不存在则创建一个文件将创建时间写入。 #!/bin/bash # 定义文件路径变量 file_path"/tmp/size.log"# 使用if语句检查文件是否存在 if [ -e "$file_path" ] # 检查变量file_path…...
事务的隔离级别和MDL
文章目录 说明不同隔离级别可能发生的现象关键现象解释MDL(元数据锁,Metadata Lock)MDL 的作用MDL 的工作原理MDL 锁的常见场景如何避免 MDL 阻塞 说明 本文章由大模型对话整理而来,如果有错误之处,请在评论区留言指正…...
用户界面软件05
已知应用 几乎所有的流行的用户界面架构都使用这种模式。我在这里举三个例子: 1. Seeheim 用户界面架构的特点是有一个应用核心的领域层和一个用户界面层。后者 被分为两层,叫做表示层和对话控制层。因为这个架构和面向事务系统有渊源,没有…...
基于Springboot + vue实现的办公用品管理系统
🥂(❁◡❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞 💖📕🎉🔥 支持我:点赞👍收藏⭐️留言📝欢迎留言讨论 🔥🔥&…...
17_Redis管道技术
Redis管道(Pipeline)技术是一种在 Redis 客户端与服务器之间进行高效数据交互的技术。 1.Redis管道技术介绍 1.1 传统请求响应模式 在传统的请求-响应模式下,客户端每发送一个命令后会等待服务器返回结果,然后再发送下一个命令。这种方式在网络延迟较高的情况下会导致性…...
【环境搭建】Metersphere v2.x 容器部署教程踩坑总结
前言 Metersphere部署过程中遇到的问题有点多,原因是其容器的架构蛮复杂的,比较容易踩坑,所以记录一下。 介绍 MeterSphere 是开源持续测试平台,遵循 GPL v3 开源许可协议,涵盖测试管理、接口测试、UI 测试和性能测…...
Vue虚拟DOM:如何提高前端开发效率
前言 随着前端技术的不断发展,越来越多的框架和库涌现出来,其中Vue.js成为了最受欢迎的前端框架之一。Vue.js采用了响应式数据绑定和组件化的思想,让开发者可以更加高效地构建交互式的用户界面。而Vue.js的底层原理涉及到许多概念和技术&…...
【C】预处理详解
在上一篇文章中,简单讲解了一个C程序是如何从一句句C代码变为一个个二进制指令,并最终变成可执行程序成功运行。在预处理、编译、汇编、链接四个步骤中,预处理阶段做的事情特别多,接下来我们就来讲解一下在预处理阶段处理的一些预…...
CES Asia 2025:VR/AR/XR引领科技新潮流
在全球科技领域蓬勃发展的大背景下,CES Asia 2025(赛逸展)即将在京盛大开幕,VR/AR/XR技术作为前沿科技的代表,将在本次展会上大放异彩,展现出令人瞩目的发展趋势和巨大潜力,同时政策优势也将为其…...
Lua调用C#
目录 创建C#入口 Lua调用类 Lua调用枚举 Lua调用数组,列表,字典 Lua调用C#拓展方法 Lua调用C#Ref与Out知识 Lua调用C#函数重载 Lua调用C#委托与事件 Lua调用C#二维数组 Lua调用C#中nil与null的差距 Lua调用C#中让系类型与lua能够互相访问 Lua调用…...
EdgeOne安全专项实践:上传文件漏洞攻击详解与防范措施
靶场搭建 当我们考虑到攻击他人服务器属于违法行为时,我们需要思考如何更好地保护我们自己的服务器。为了测试和学习,我们可以搭建一个专门的靶场来模拟文件上传漏洞攻击。以下是我搭建靶场的环境和一些参考资料,供大家学习和参考࿰…...
springboot使用Easy Excel导出列表数据为Excel
springboot使用Easy Excel导出列表数据为Excel Easy Excel官网:https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write 主要记录一下引入时候的pom,直接引入会依赖冲突 解决方法: <!-- 引入Easy Excel的依赖 -->&l…...
现代 CPU 的高性能架构与并发安全问题
现代 CPU 的设计(如多级缓存、指令重排)为了提升性能,引入了许多优化机制,但这些机制可能导致并发场景下的安全性问题。并发安全性主要体现在三个方面:原子性、有序性 和 可见性。这些问题在底层通过 CAS(C…...
【数模学习笔记】插值算法和拟合算法
声明:以下笔记中的图片以及内容 均整理自“数学建模学习交流”清风老师的课程资料,仅用作学习交流使用 文章目录 插值算法定义三个类型插值举例插值多项式分段插值三角插值 一般插值多项式原理拉格朗日插值法龙格现象分段线性插值 牛顿插值法 Hermite埃尔…...
JavaScript 数组及其常用方法
1. JavaScript 数组概述 数组是 JavaScript 中用于存储多个值的数据结构。它可以存储不同类型的元素,并提供强大的方法来操作和管理数据。数组的元素按索引(从 0 开始)进行访问。 2. 数组的创建方式 1) 使用数组字面量 let fruits [&quo…...
SQL HAVING 子句深入解析
SQL HAVING 子句深入解析 介绍 SQL(Structured Query Language)是一种用于管理关系数据库管理系统的标准编程语言。在SQL中,HAVING子句是与GROUP BY子句一起使用的,用于筛选分组后的数据。它根据聚合函数的结果对组进行条件过滤…...
vue3+ts的几个bug调试
由于编译问题,把几个type检查给关闭了,否则错误太多。 1)第一个检查出的问题,拼写错误数组的length,写成了lengh。 2)数组的对象引用。 torStatus Array(8).fill({ ...defaultStatus }) as TorStatus[]…...
git: hint:use --reapply-cherry-picks to include skipped commits
问: 当我在feture分支写完功能,切换到dev更新了远端dev代码,切回feture分支,git rebase dev分支后出现报错: warning skipped previously applied commit 709xxxx hint:use --reapply-cherry-picks to include skippe…...
Microsoft Sql Server 2019 数据类型
数据类型 bigint、int、smallint、tinyint 使用整数数据的精确数字数据类型。 若要节省数据库空间,请使用能够可靠包含所有可能值的最小数 据类型。 例如,对于一个人的年龄,tinyint 就足够了,因为没人活到 255 岁以上。 但对于建筑物的 年龄,tinyint 就不再适应,因为建…...
C++实现设计模式---代理模式 (Proxy)
代理模式 (Proxy) 代理模式 是一种结构型设计模式,它为其他对象提供一个代理以控制对该对象的访问。代理模式常用于延迟加载、访问控制、智能引用等场景。 意图 提供对某对象的控制。控制对目标对象的访问,通常用于在不改变目标对象的情况下࿰…...
微信小程序用的SSL证书有什么要求吗?
微信小程序主要建立在手机端使用,然而手机又涉及到各种系统及版本,所以对SSL证书也有要求,如果要小程序可以安全有效的访问需要满足以下要求: 1、原厂SSL证书(原厂封)。 2、DV单域名或者DV通配符。 3、兼…...
Flutter中Get.snackbar和Get.dialog关闭冲突问题记录
背景: 在使用GetX框架时,同时使用了Get.snackbar提示框和Get.dialog加载框,当这两个widget同时存在时,Get.dialog加载框调用Get.back()无法正常关闭。 冲突解释: 之所以会产生冲突,是因为Get.snackbar在关…...
命令模式-Command Pattern
什么是命令模式 命令模式是一种行为类设计模式,核心是将每种请求或操作封装为一个独立的对象,从而可以集中管理这些请求或操作,比如将请求队列化依次执行、或者对操作进行记录和撤销。 命令模式通过将请求的发送者(客户端)和接收者(执行请求…...
【Linux笔记】Day1
基于韩顺平老师课程记录: https://www.bilibili.com/video/BV1Sv411r7vd 安装CentOS 给CentOS手动分区 分为三个区: boot分区(给1G就行) 交换分区(和内存相关,这里和虚拟机的内存2G一致) …...
如何明智地提问
如何明智地提问的重要总结,让我为主要观点添加一些具体的实践建议: 提问前的准备工作 尝试在 Google、Stack Overflow 等平台搜索相似问题阅读相关文档和错误日志尝试自己调试和排查问题记录下已尝试过的解决方案 选择合适的提问平台 Stack Overflow…...
【大前端】Vue3 工程化项目使用详解
目录 一、前言 二、前置准备 2.1 环境准备 2.1.1 create-vue功能 2.1.2 nodejs环境 2.1.3 配置nodejs的环境变量 2.1.4 更换安装包的源 三、工程化项目创建与启动过程 3.1 创建工程化项目 3.2 项目初始化 3.3 项目启动 3.4 核心文件说明 四、VUE两种不同的API风格 …...