【C语言】函数(四):函数递归与迭代,二者有什么区别
目录
- 前言
- 递归
- 定义
- 递归的两个必要条件
- 接受一个整型值(无符号),按照顺序打印它的每一位
- 使用函数不允许创建临时变量,求字符串“abcd”的长度
- 求n的阶乘
- 求第n个斐波那契数
- 迭代
- 总结
- 递归与迭代的主要区别
- 用法不同
- 结构不同
- 时间开销不同
- 两个经典问题
前言
从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事!故事是什么?“从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事!故事是什么?‘从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事!故事是什么?……’”
递归
定义
计算机科学中,递归是是指在函数的定义中使用函数自身的方法。它通常把一个大型的复杂问题层层转化为一个与原问题相似的规模较小的问题来求解,递归只需要少量的代码就可以描述出解题过程中的多次重复计算,大大减少了程序的代码量。
递归的主要思想是:大化小
递归的两个必要条件
1.存在限制条件,当满足这个限制条件时,递归停止。
2.每次递归调用之后越来越接近限制条件。
错误示例:
#include<stdio.h>int main()
{printf("hello world!\n");main();return 0;
}

画红线的地方,意思是栈溢出,从上面写的程序中发现,递归没有停止的限制条件,导致死递归。
接受一个整型值(无符号),按照顺序打印它的每一位
示例1:
问题描述:
接受一个整型值(无符号),按照顺序打印它的每一位。
样例输入:1234
样例输出:1 2 3 4
代码示范:
#include<stdio.h>void Func(unsigned int x)
{if (x > 9){Func(x / 10);}printf("%d ", x % 10);
}
int main()
{unsigned int num = 0;scanf("%d", &num); //假设输入的是123Func(num);return 0;
}

到这里对递归应该有一个比较清晰的认识了,在图中红色过程表示的就是递归当中的“递”,蓝色过程表示的就是递归当中的“归”。
使用函数不允许创建临时变量,求字符串“abcd”的长度
示例2:
问题描述:
使用函数不允许创建临时变量,求字符串“abcd”的长度
分析: 直接求字符串“abcd”的长度,它是字符串,在前面文章中说过字符串的结束标志是 \0
代码展示:
#include<stdio.h>
#include<string.h>int Length(char* l)
{int count = 0;while (*l != '\0'){count++;l++;}return count;
}
int main()
{char arr[] = "abcd";int len = Length(arr);printf("%d\n", len);return 0;
}
这段代码完全没有问题,但是题目要求不允许创建临时变量,这里创建了临时变量count。
再分析:

所以我们可以将Length函数写成递归的形式:
//递归
int Length(char* length)
{if (*length == '\0')return 0;elsereturn 1 + Length(length + 1);
}
我们再分析过程:

求n的阶乘
我们回顾一下n!怎么算:
#include<stdio.h>int Func(int x)
{int i = 0;int j = 1;for (i = 1; i <= x; i++){j *= i;}return j;
}
int main()
{int n = 0;scanf("%d", &n);int result = Func(n);printf("%d\n", result);return 0;
}
上述代码是非递归的形式,我们再来思考一下如何使用递归来写n!:
我们可以发现n!=n*(n-1)!
所以Func函数可以写成:
int Func(int x)
{if (x <= 1)return 1;else return x * Func(x - 1);
}
求第n个斐波那契数
斐波那契数列由0和1开始,之后的斐波那契数就是由之前的两数相加而得出。
前几个斐波那契数是:
1、 1、 2、 3、 5、 8、 13、 21、 34、 55、 89、 144……
也就是说从第三个数开始,后面的每一个斐波那契数都是前两个数的和。
到这里就可以直接写代码了:
#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 result = Fib(n);printf("%d\n", result);return 0;
}
这时候我们信心满满,让计算机帮我求第50个斐波那契数,当我们执行程序后在键盘输入50,却发现,等了很久都没有发现它输出内容。
为什么?
我们要求第50个斐波那契数,就需要计算第49个和第48个数,计算第49个数又需要计算第48个数和第47个数,可以想一下上面这个图画到末端需要画多少,除了前两个数,要算2的48次方,而int型只占4个字节的内存,也就是32位,2的32次方都已经42亿多,可想而知计算量非常庞大。按照递归的方式要进行大量的重复计算。我们可以做一个计数,计算第40个斐波那契数中3被计算了多少次,你会发现3被计算了将近四千万次,效率非常低。所以不是因为计算机偷懒没算,它也在拼命地计算,只是量太大,它一会儿也算不出来。
那么应该如何改进呢?
迭代
在计算机科学中,迭代是程序中对一组指令(或一定步骤)的重复。它既可以被用作通用的术语(与“重复”同义),也可以用来描述一种特定形式的具有可变状态的重复。可以简单理解为普通循环。但与普通循环有所差别,迭代时,循环代码中参与运算的变量同时是保存结果的变量,当前保存的结果作为下一次循环计算的初始值
我们知道函数形参是被存放在栈区当中,递归每“递”一次,就要开辟一个变量的内存,那么当参数非常大的时候,栈区内存不够了,栈区放不下了,也就是说栈区空间已经被耗尽了,但是你的东西还没放完,这个时候就会出现栈溢出的现象。
那么我们回头再看求第n个斐波那契数,能否将递归转换成迭代的形式。
while (n >= 3){c = a + b;a = b;b = c;n--;}return c;
那么当n小于3的时候,也就是第1个或者第2个数,都是1,所以在给a,b,c初始化的时候,都赋值为1即可。
完整代码:
#include<stdio.h>
//迭代
int Fib(int n)
{int a = 1;int b = 1;int c = 1;while (n >= 3){c = a + b;a = b;b = c;n--;}return c;
}
int main()
{int n = 0;scanf("%d", &n);int result = Fib(n);printf("%d\n", result);return 0;
}
这个时候程序运行后输入50,尽管结果仍然是错误的,但是速度非常快。解决了大量重复计算的问题。
总结
当使用递归可能会导致栈溢出时,程序效率明显下降的时候,就不能够使用递归了。
如何解决?
1.可以使用迭代替换递归。
2.在递归函数设计中可以使用static限制变量,让变量申请一块内存后,在那一块内存折腾。不仅不再大量开辟栈区内存,从而导致栈溢出,并且static可以保存递归时的中间状态,并且为各个调用层所访问。
- 递归代码量少,迭代不易想到,递归比迭代更清晰。所以许多问题采用递归的方式解释。
- 迭代实现比递归实现的代码可读性差,但是效率高。
- 当问题复杂的时候,难以用迭代实现,此时使用递归会更加简洁。
递归与迭代的主要区别
用法不同
- 迭代是代码块的重复。虽然需要更多的代码,但时间复杂度通常小于递归的时间复杂度。
- 递归是多次调用自身,因此代码长度非常小。但是,当有非常非常多次的递归调用时,递归的时间复杂度可能会呈指数级增长。
结构不同
- 迭代是环结构,从初始状态开始,每次迭代都遍历这个环,并更新状态,多次迭代直到到达结束状态。
- 递归是树结构,从字面可以理解为重复“递”和“归”的过程,当“递”到达底部时就会开始“归”。
时间开销不同
- 与迭代相比,递归具有大量的开销。递归具有重复函数调用的开销,即由于重复调用同一函数,代码的时间复杂度增加了许多倍。
两个经典问题
- 汉诺塔问题
- 青蛙跳台阶问题
相关文章:
【C语言】函数(四):函数递归与迭代,二者有什么区别
目录 前言递归定义递归的两个必要条件接受一个整型值(无符号),按照顺序打印它的每一位使用函数不允许创建临时变量,求字符串“abcd”的长度求n的阶乘求第n个斐波那契数 迭代总结递归与迭代的主要区别用法不同结构不同时间开销不同…...
[原创](免改BIOS)使用Clover升级旧电脑-(高阶玩法)让固态硬盘内置Win11 PE启动系统
[简介] 常用网名: 猪头三 出生日期: 1981.XX.XXQQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi…...
React项目中发生空白但不报错的原因分析和解决?
文章目录 前言组件渲染问题状态管理问题异步操作问题代码错误但未抛出异常如果我们使用的是chorme浏览器的话,可以下载一个开发者工具,例如下图:代码审查使用调试工具日志和输出检查外部依赖异步操作终极大法,不到万不得已不可以使…...
redis运维(十七)事务
一 redis事务 事务核心参考 ① 基础概念 1、场景引入核心:通过现象思考原因? 2、事务的概念 3、事务四大特性说明: redis只具备部分特性 重点1: 原子性和一致性 重点2: 隔离性和持久性 ② redis的事务 1、基础铺垫备注&…...
Vue框架学习笔记——Vue实例中el和data的两种写法
文章目录 前文提要Vue实例的el第一种写法第二种写法小结 Vue实例中data第一种写法,对象式效果图片第二种写法,函数式效果图片小结 前文提要 本文仅做自己的学习记录,如有错误,请多谅解 Vue实例的el 第一种写法 <body><…...
libbz2 for Mac OS makefile
git地址:git://sourceware.org/git/bzip2.git a文件Makefile # ------------------------------------------------------------------ # This file is part of bzip2/libbzip2, a program and library for # lossless, block-sorting data compression. # # bzip…...
测试工具JMeter的使用
目录 JMeter的安装配置 测试的性能指标 TPS 响应时长 并发连接 和 并发用户 CPU/内存/磁盘/网络 负载 性能测试实战流程 JMeter JMeter快速上手 GUI模式 运行 HTTP请求默认值 录制网站流量 模拟间隔时间 Cookie管理器 消息数据关联 变量 后置处理器 CSV 数据文…...
C++编程——输入
#include<bits/stdc.h> using namespace std; int main(){//beginint a 0, b 0, c 0, d 0, e 0;char f1, f2;char g[30];scanf("%d", &a); //输入整数并赋值给变量ascanf("%d", &b); //输入整数并赋值给变量bscanf("%d", &…...
opencv-直方图
直方图是一种对图像亮度分布的统计表示,它显示了图像中每个灰度级别的像素数量。在OpenCV中,你可以使用cv2.calcHist() 函数计算直方图。 以下是一个简单的示例,演示如何计算和绘制图像的直方图: import cv2 import numpy as np …...
el-table表格排序(需要后端判别),el-table导出功能(向后端发送请求)
(1)表格排序 (2)简单的table导出功能(需要后台支撑)必须要有iframe (3)页面所有代码: <template><div class"mainContainer"><el-form:model&…...
【MATLAB】全网入门快、免费获取、持续更新的科研绘图教程系列2
14 【MATLAB】科研绘图第十四期表示散点分布的双柱状双Y轴统计图 %% 表示散点分布的双柱状双Y轴统计图%% Made by Lwcah (公众号:Lwcah) %% 公众号:Lwcah %% 知乎、B站、小红书、抖音同名账号:Lwcah,感谢关注~ %% 更多…...
git与ssh多账户共存
git与ssh多账户共存 前言git多账户ssh多公钥参考 前言 在使用git与ssh时,经常会遇到多个账户共存的情况 例如使用不同的公钥登陆到不同的服务;使用不同的git信息进行commit git多账户 在默认情况下 git的信息存在 ~/.gitconfig 可以使用命令查看 git…...
BLE协议栈入门学习
蓝牙LE栈 物理层 频带 蓝牙LE在2400MHz到2483.5MHz范围内的2.4GHz免授权频段工作,该频段分为40个信道,每个信道间隔为2MHz。 时分 蓝牙LE是半双工的,可以发送和接收,但不能同时发送和接收,然而,所有的设…...
【反射】简述反射的构造方法,成员变量成员方法
🎊专栏【JavaSE】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 🥰欢迎并且感谢大家指出我的问题 文章目录 🎄什么是反射🎄获取class对象的三种方式⭐代码实现 dz…...
acwing算法基础之数学知识--求卡特兰数
目录 1 基础知识2 模板3 工程化 1 基础知识 题目:给定n个0和n个1,它们将按照某种顺序排成长度为2n的序列,求它们能排成的所有序列中,能够满足任意前缀序列中0的个数都不少于1的个数的序列有多少个? 输出的答案对 1 0 …...
《洛谷深入浅出基础篇》P4017最大食物链————拓扑排序
上链接:P4017 最大食物链计数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P4017 上题干: 题目背景 你知道食物链吗?Delia 生物考试的时候,数食物链条数的题目全都错了,因为她总是…...
设置定时自动请求测试_自动定时循环发送http_post请求---postman工作笔记001
其实就是创建接口文件夹的时候,有个monitor collection 用来监听接口执行情况,这里就可以设置 可以看到多久执行一次对吧,这里可以设置每几分钟执行一次,一共执行多少次等等 但是这里要说明一下,如果需要使用monitor功能,必须需要登录, 所以如果这里点击monitor collection…...
Vue3封装全局插件
定义一个全局加载组件 一、首先定义dom元素 定义一个index.vue文件 <template><div class"loading">loading...</div> </template> <script setup lang"ts"></script> <style scoped> .loading {display: fl…...
【Python 训练营】N_6 求素数
题目 判断101-200之间有多少个素数,并输出所有素数。 分析 判断素数的方法:用一个数分别去除2到sqrt(这个数),如果能被整除,则表明此数不是素数,反之是素数。 答案 h 0 leap 1 from math import sqrt from sys …...
【图论】关键路径求法c++
代码结构如下图: 其中topologicalSort(float**, int, int*, bool*, int, int)用来递归求解拓扑排序,topologicalSort(float**, int*&, int, int, int)传参图的邻接矩阵mat与结点个数n,与一个引用变量数组topo,返回一个布尔值…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...



