C语言:递归思想及实例详解
简介:在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法。通过函数的自调用化繁为简。
递归可以说是编程中最神奇的一种算法。因为我们有时候可能不能完全明晰代码的运行过程,但是我们却知道代码可以跑出正确的结果。而当我们使用其他算法,我们必须将代码运行的每一个细节都弄清楚才能确保代码的正确性。这就是递归的神奇之处。
下面由浅入深对递归进行解析:
1.阶乘计算
相信这是每个人初学递归时都会遇到的问题。当然这也是最容易理解的一种递归,思路很简单:
如果要计算n的阶乘,我们可以先算出n-1的阶乘再乘上n,如果要计算n-1的阶乘,我们可以先算出n-2的阶乘再乘上n-1,依此类推,递归逐渐深入,我们只需要给出1的阶乘和0的阶乘,然后递归就会不断返回,最后算出n的阶乘。
这一点还是比较容易理解,此处不作赘述。
int f(int n)
{if (n == 0 || n == 1)return 1;elsereturn n * f(n - 1);
}
2.爬楼梯
爬楼梯OJ
题目概述:
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
这也是一个典型可以用递归解决的问题
思路:如果我们要到达n阶,我们可以从n-1阶往上跳一个台阶,或者从n-2阶往上跳2个台阶,我们可以想想是不是只有这两种情况?所以到达n阶的方法数量是不是就是到达n-1阶的方法数量和到达n-2阶的方法数量之和?答案是显然的。那么这个问题是不是就和第一个例题类似了,只是这里的递归分支了,但是本质上是相同的,我们只需要给出到达1阶和到达2阶的方法数量,递归就能返回到n阶的方法数量。
int climbStairs(int n){if(n==1)return 1;else if(n==2)return 2;elsereturn climbStairs(n-1)+climbStairs(n-2);
}
需要注意的是:这里给出的代码并不能通过,因为分支递归在递归层数过深的时候很容易超时,举个例子,我们要计算到达10阶的方法数,我们要算9阶和8阶.......

这里递归的时间复杂度是O(2^n),n过大时超时是必然的。如果要避免超时可以使用迭代(循环)代替递归。使用递归的优势是代码逻辑更加明了,而迭代的优势是速度更快,如果递归分支了那么迭代的优势会更加明显。
这里给出迭代的方法仅供参考,不作讨论
int climbStairs(int n){if(n==1)return 1;if(n==2)return 2;int a=1;int b=2;int c;while(n>=3){c=a+b;a=b;b=c;n--;}return c;
}
3.找最大值
要求很简单,写一个函数
int find(int*nums,int numsSize)
要求返回nums数组的最大值。最简单的方法是定义一个max变量遍历nums并更新max最后返回即可。如果使用递归该怎么做呢?
思路:我们将nums平分为左右两个部分,记左半部分最大值为max1,右半部分最大值为max2,那么显然整个nums的最大值是max1和max2中较大的一个。那么同样的,max1和max2是如何得到的呢?我们将左右部分分别再次平分........不断进行下去,知道最后平分完之后一个元素单独组成一个部分,那么这个部分的最大值就是这个单独的元素
int max(int* nums, int numsSize)
{int right = numsSize - 1;if (right == 0)return nums[0];else{int mid = right / 2;return max(nums, mid + 1) > max(nums + mid + 1, right -mid)? max(nums, mid + 1): max(nums + mid + 1, right - mid);}
}
以下是一个示意图:

教学视频:
在这里强烈推荐一个视频,视频链接,看完这个视频会对递归以及接下来的问题有更深刻的认识
4.归并排序
从这个部分开始递归就上了一个档次,第三个问题其实是这个问题的铺垫
归并排序的原理:与第三个问题一样,将数组细分直到每个部分只有一个元素,此时每个部分可以看作升序,使用merge函数将两部分合并成一个升序的部分(merge函数是将[4,8,9,10,1,2,3]这样两个部分为升序的数组排成完全升序的数组,力扣上有类似的实现merge函数的OJ 合并两个有序数组,但是这里是仅处理一个数组,只需要进行一些细节处理就能转换成相同问题)
归并排序的实现
void sort(int* arr1, int left, int right)
{if (right == left)return;int mid = left + (right - left) / 2;//将左半部分排序sort(arr1, left, mid);//将右半部分排序sort(arr1, mid + 1, right);//将两个升序部分排成完全升序merge(arr1, left, mid, right);
}
merge的实现
void merge(int* arr, int start, int mid, int end)
{int* copy = (int*)malloc(sizeof(int) * (end -start+ 1));memcpy(copy, arr+start,(end-start+1)*sizeof(int));int count = start;int count1 = 0;int count2 = mid-start+1;while (count1 <= mid-start && count2 <= end-start){if (copy[count1] <= copy[count2]){arr[count++] = copy[count1++];}else{arr[count++] = copy[count2++];}}if(count1==mid-start+1){while (count2 <= end-start){arr[count++] = copy[count2++];}}else{while (count1 <= mid-start){arr[count++] = copy[count1++];}}free(copy);
}
在上边给出的教学视频里包含详细的动画展示,这里就不画图进行模拟了。
5.汉诺塔问题

有A、B、C三根柱子,初始状态A柱子上有若干个盘子,目标是将A上的所有盘子移动到C柱子上,并且小盘子在上,大盘子在下,与初始状态相同。移动过程中大盘子不能放在小盘子上。
我们设计一个函数
void hano(int n,char a,char b,char c)
很多人对函数的四个参数不理解,或者理解不深刻,包括一些博文对几个参数都没有很好的解释。在这里先进行一个简单的分析,在后面的代码中会深入剖析。
参数分析:
很明显n表示A柱子上有n个盘子,奇怪的是为什么要三个char类型变量???其实很简单,这里的三个char类型形参接收的分别常量'A' 'B' 'C'中的一个。这个函数的含义是:将a变量的柱子上的n-1个盘子移动到b变量的盘子上,再将a变量柱子上第n个(最大的)盘子移动到c变量柱子上,最后将b变量柱子上的n-1个盘子移动到c变量柱子上。举个例子:
hano(15,'B','A','C');
表示将B柱子上的14个盘子移动到A,再把B上最后一个盘子移动到C上,再把A上n-1个盘子移动到C上
但是n-1个盘子是不能直接移动的,所以具体是怎么移动的呢?
当n=2时,我们会使用这个函数
hano(2,'A','B','C');
这样n-1=1,我们每次只会移动一个盘子,就可以直接移动了。
当n=3时,我们需要把2个盘子从A移动到B上,于是我们使用
hano(n-1,'A','C','B');
那么怎么把2个盘子移动到B上?我们首先要把1个盘子移动到C上
那么对于n个盘子,我们的思路也一样
//move中的n表示的是 第n个盘子
void move(int n, char soure, char destination)
{printf("move %d from %c to %c\n", n, soure, destination);
}
void hano(int n,char a,char b,char c)
{if (n == 1){move(1, a, c);}else{//将n-1个盘子从a移动到b上//问题:既然是从a到b,那么和第二个参数有什么关系呢?为什么需要第二个参数?hano(n - 1, a, c, b);move(n, a, c);//只有一个盘子了,直接打印出移动轨迹hano(n - 1, b, a, c);}
}
现在对代码中提出的问题进行解答:我们再hano函数中再次调用hano,三个参数的值是会来回hano知道这一步的三hano才能推测下一步的三个参数,少一个参数hano函数就不能正常递归了
到了这里文章就结束了,最后再次推荐大家看一看文章给出的视频,看完会对递归有更深刻的认识。
相关文章:
C语言:递归思想及实例详解
简介:在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法。通过函数的自调用化繁为简。 递归可以说是编程中最神奇的一种算法。因为我们有时候可能不能完全明晰代码的运行过程,但是我们却知道代码可以跑出正确的结果。而当我们使…...
好题分享0
P2141 [NOIP2014 普及组] 珠心算测验 原题链接 : [NOIP2014 普及组] 珠心算测验 - 洛谷 思路 : 用哈希表来存出现过的两数之和,最后ans即可 代码 : #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define end…...
python的asyncio事件循环
一、介绍 asyncio是Python标准库中的一个异步编程框架,它提供了一个事件循环(event loop),用于协调异步任务的执行和结果的返回。在asyncio中,事件循环是一个非常重要的概念,它是异步编程的核心。 事件循…...
QT day1登录界面设计
要设计如下图片: 代码如下: main.cpp widget.h widget.cpp 运行效果: 2,思维导图...
(一)KITTI数据集用于3D目标检测
KITTI数据集介绍 数据基本情况 KITTI是德国卡尔斯鲁厄科技学院和丰田芝加哥研究院开源的数据集,最早发布于2012年03月20号。 对应的论文Are we ready for Autonomous Driving? The KITTI Vision Benchmark Suite发表在CVPR2012上。 KITTI数据集搜集自德国卡尔斯鲁厄市&…...
手写Promise完整介绍
Promise是一种用于处理异步操作的机制,它可以将异步操作的结果以同步的方式进行处理和返回。在JavaScript中,Promise是一种内置对象,但我们也可以手动实现一个Promise类来更好地理解其原理和工作方式。 Promise的特性 首先,让我…...
【kubernetes系列】Calico原理及配置
概述 Calico是针对容器,虚拟机和基于主机的本机工作负载的开源网络和网络安全解决方案。 Calico支持广泛的平台,包括Kubernetes,OpenShift,Docker EE,OpenStack和裸机服务。 Calico在每个计算节点都利用Linux Kernel实…...
RabbitMQ 的快速使用
docker部署rabbitmq # management才有管理页面 docker pull rabbitmq:management# 新建容器并运行 docker run \-e RABBITMQ_DEFAULT_USERadmin \ -e RABBITMQ_DEFAULT_PASSadmin \ -v mq-plugins:/plugins \--name mq \--hostname mq \-p 15672:15672 \-p 5672:5672 \-itd \ra…...
VUE3添加全局变量
全局变量的添加 在vue3.0中注入全局方法不是在prototype上挂载了,而是添加在config.globalProperties属性上。 //main.js import { createApp } from "vue"; import App from "./App.vue";const app createApp(App); app.config.globalPrope…...
JavaScript基础语法01——初识JavaScript
哈喽,大家好,我是雷工! 最近有项目用到KingFusion软件,由于KingFusion是B/S架构的客户端组态软件,因此在学习KingFusion产品时会涉及许多前端的知识。 像JavaScript语言就是需要用的,俗话说:活到…...
家宽用户家庭网的主要质量问题是什么?原因有哪些
1 引言 截至2020年底,我国家庭宽带(以下简称“家宽”)普及率已达到96%。经过一年多的发展,当前,家庭宽带的市场空间已经饱和。运营商在家宽市场的竞争也随之从新增用户数的竞争转移到家宽品质的竞争。 早期运营商的家…...
ZooKeeper的典型应用场景及实现
文章目录 1、典型应用场景及实现1.1、 数据发布/订阅1.1.1、配置管理案列 1.2、负载均衡1.3、命名服务1.4、分布式协调/通知1.4.1、一种通用的分布式系统机器间通信方式 1.5、集群管理1.6、Master选举1.7、分布式锁1.7.1、排他锁1.7.2、共享锁 1.8、分布式队列 2、ZooKeeper在大…...
智能安全帽~生命体征检测与危险气体检测一体化集成设计还是蓝牙无线外挂式方式好?
生命体征(心率、血氧等)检测&上报平台,危险气体采集&上报平台,是智能安全帽产品中常见的两种选配件,它们的实现有两种典型的模式: 1)将传感器集成到主板上,做成一体化的智能…...
【Java并发】聊聊对象内存布局和syn锁升级过程
对象存储解析:一个空Object对象到底占据多少内存? 对象内存布局 Mark Word占用8字节,类型指针占用8个字节,对象头占用16个字节。 好了,我们来看一下一个Object对占用多少空间, 因为java默认是开启压缩…...
【档案专题】八、电子档案鉴定与销毁
导读:主要针对电子档案鉴定与销毁相关内容介绍。对从事电子档案管理信息化的职业而言,不断夯实电子档案管理相关理论基础是十分重要。只有通过不断梳理相关知识体系和在实际工作当中应用实践,才能走出一条专业化加职业化的道路,从…...
进程与子进程
一、子进程 1.fork()创建子进程 一个现有的进程可以调用 fork()函数创建一个新的进程,调用 fork()函数的进程称为父进程,由 fork()函数创建出来的进程被称为子进程(child process)。(使用该函数需要包含头文件<uni…...
如何对MySQL和MariaDB中的查询和表进行优化-提升查询效率
前言 MySQL和MariaDB是数据库管理系统的流行选择。两者都使用SQL查询语言来输入和查询数据。 尽管SQL查询是简单易学的命令,但并不是所有的查询和数据库函数都具有相同的效率。随着你存储的信息量的增长,如果你的数据库支持一个网站,随着网…...
【Android】关于binder_calls_stats服务
Android 9上有了binder_calls_stats服务,提供了java层的binder统计, Android中的Binder Call Stats(Binder调用统计)是一项用于监控和记录Android系统中Binder通信的统计信息的功能。Binder是Android中的一种进程间通信ÿ…...
给前端返回http链接,由于浏览器缓存不能获取到最新资源怎么办?
1、问题描述 今天在工作中接到这样一个需求,接收前端的图片文件并上传到远程,将原有图片覆盖并返回一个http链接以供前端展示。用户使用后反馈没有修改成功,上了远程拉图片发现已经修改了,但是用户浏览器还是老的图片。排查原因是…...
【Java Web】检查用户登录状态,防止用户访问到非法页面
使用拦截器 在方法前标注自定义注解拦截所有请求,只处理带有该注解的方法 自定义注解: 常用元注解:Target, Rentention, Document, Inherited如何读取注解: - Method.getDeclaredAnnotations() - Method.getAnnotaion(Class<T&…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
