函数递归专题(案例超详解一篇讲通透)

函数递归

    • 前言
      • 1.递归案例:
        • 案例一:取球问题
        • 案例二:求斐波那契额数列
        • 案例三:函数实现n的k次方
        • 案例四:输入一个非负整数,返回组成它的数字之和
        • 案例五:元素逆置
        • 案例六:实现strlen
        • 案例七:爬楼梯1.0
        • 案例八:爬楼梯2.0
        • 案例九:求阶乘
        • 案例十:求阶乘和
        • 案例十一:杨辉三角
        • 案例十二:最大公约数
        • 案例十四:汉偌塔
      • 2.递归与迭代
      • 3.何时使用递归

前言

程序调用自身的编程技巧称为递归( recursion)。
递归做为一种算法在程序设计语言中广泛应用。
一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
递归策略
只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的主要思考方式在于把大事化小

递归的两个必要条件

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用之后越来越接近这个限制条件

1.递归案例:

案例一:取球问题

在 n 个球中,任意取 m 个(不放回),求有多少种不同取法。

分析:

假设有一个特殊球,此球的状态只有两种:被取到和没有被取到。
若被取到,那么只需在n-1个球中取m-1个球。
若没有被取到,需在n-1个球中取m个球。

代码演示: 
int ball(int n, int m)
{if (m > n)return 0;if (n == m)return 1;if (m == 0)return 1;return ball(n - 1, m - 1) + ball(n - 1, m);
}
int main()
{int n = 0;int m = 0;scanf("%d%d", &n, &m);printf("%d\n", ball(n, m));return 0;
}

运行结果:
在这里插入图片描述

案例二:求斐波那契额数列

这个数列从第3项开始,每一项都等于前两项之和。

分析:

在数学上,斐波那契数列以如下被以递推的方法定义:
在这里插入图片描述
在这里插入图片描述

代码演示:
int Fib(int n)
{if (n <= 2)return 1;elsereturn Fib(n - 1) + Fib(n - 2);
}
int main()
{int n = 0;scanf("%d", &n);//20int ret = Fib(n);printf("%d\n", ret);return 0;
}

运行结果:
在这里插入图片描述

案例三:函数实现n的k次方

分析

在这里插入图片描述
指数为负数用double(%lf打印)

代码演示:
double Pow(n, k)
{if (k > 0){return n * Pow(n, k-1);}else if(k == 0){return 1;}else{return 1.0 / Pow(n, -k);//实现指数为负数}}
int main()
{int n = 0;int k = 0;scanf("%d %d", &n, &k);double ret = Pow(n, k);printf("%lf\n", ret);//double打印用lfreturn 0;
}

运行结果:
在这里插入图片描述

案例四:输入一个非负整数,返回组成它的数字之和

分析:

当一个数是大于0 的数时,要得结果等于这个数模(%)10得到最低位的数字,然后再加它的次低位…一直加到最高位的数字,这些数字用给这个数除以(10)得到,递归调用这个函数,即可。

代码演示:
int DigitSum(int n)
{if (n < 9){return n;}else{return DigitSum(n / 10) + n % 10;}
}
int main()
{int n = 0;scanf("%d", &n);int ret = DigitSum(n);printf("%d\n", ret);return 0;
}

运行结果:
在这里插入图片描述

案例五:元素逆置

分析:

在这里插入图片描述

代码演示:
#include<string.h>
void reverse_string(char* str)
{size_t len = strlen(str);char temp = str[0];str[0] = str[len - 1];str[len - 1] = '\0';if (strlen(str+1) >= 2){reverse_string(str+1);}str[len - 1] = temp;
}
int main()
{char arr[] = "abcdef";reverse_string(arr);printf("%s\n", arr);//字符串用%s打印return 0;
}

运行结果:
在这里插入图片描述

案例六:实现strlen

分析:

在这里插入图片描述

代码演示:
size_t my_strlen(char* str)
{if (*str == '\0')//(str==0)return 0;elsereturn 1 + my_strlen(str + 1);
}
int main()
{char arr[] = "abcdef";size_t len = my_strlen(arr);printf("%zd", len);return 0;
}

运行结果:
在这里插入图片描述

案例七:爬楼梯1.0

树老师爬楼梯,他可以每次走 1 级或者 2 级,输入楼梯的级数,求不同的走法数。

分析:

如果从第0级台阶爬到第1级台阶:有1种方法(爬1个台阶)
如果从第0级台阶爬到第2级台阶:有2种方法(爬1个台阶 或 爬2个台阶)
如果从第0级台阶爬到第3级台阶:有3种方法
先从第0级台阶爬到第1级台阶,再从第1级台阶爬到2级台阶,再从第2级台阶爬到第3级台阶,即1,1,1
先从第0级爬1个台阶到第1级台阶,再从第1级爬2个台阶到第3级,即1,2
先从第0级爬2个台阶到第2级台阶,再从第2级爬1个台阶到第3级,即2,1
如果从第0台阶爬到第4级台阶:有5种方法
1,1,1,1
1,1,2
1,2,1
2,1,1
2,2
归纳发现原理同:斐波那契数列

代码演示:
int stair(int n)
{if (n == 1)return 1;if (n == 2)return 2;return stair(n - 1) + stair(n - 2);
}
int main()
{int n = 0;scanf("%d", &n);printf("%d\n", stair(n));return 0;
}

运行结果:
在这里插入图片描述

案例八:爬楼梯2.0

树老师爬楼梯,他可以每次走 1 级、2 级或者 3 级,输入楼梯的级数,求不同的走法数。

原理同上

代码演示:
int stair(int n)
{if (n == 1)return 1;if (n == 2)return 2;if (n == 3)return 4;return stair(n - 1) + stair(n - 2) + stair(n - 3);
}
int main()
{int n = 0;scanf("%d", &n);printf("%d\n", stair(n));
}

运行结果:
在这里插入图片描述

案例九:求阶乘

代码演示:
int Fac(int n)
{if (n <= 1)return 1;elsereturn n* Fac(n - 1);
}
int main()
{int n = 0;scanf("%d", &n);int r = Fac(n);printf("%d\n", r);return 0;
}

运行结果:
在这里插入图片描述

案例十:求阶乘和

求 1!+2!+3!+4!+5!+6!+7!+…+n!的和。

代码演示:
int factorial(int n)
{if (n == 1)return 1;return n * factorial(n - 1);
}
int main()
{int n = 0;int sum = 0;int i = 0;scanf("%d", &n);for (i = 1; i <= n; i++){sum += factorial(i);}printf("%d\n", sum);return 0;
}

运行结果:
在这里插入图片描述

案例十一:杨辉三角

输入要打印的层数,打印杨辉三角
在这里插入图片描述

分析

根据观察第一列和对角线上的元素之外,其余元素的值均为前一行上的同列元素和前一列元素之和。(我们可以依靠递归相加就行实现)

#include <stdio.h>
long Tri(int r, int c)    
{return (c == 1 || c == r) ? 1 : Tri(r - 1, c - 1) + Tri(r - 1, c);
}
int main()
{int i = 0;int j = 0;int n = 0;scanf("%d", &n);for (i = 1; i <= n; i++)	// 输出n行{for (j = 0; j < n - i; j++)		//每行前面补空格,显示成等腰三角形	printf("   ");for (j = 1; j <= i; j++)printf("%6d", Tri(i, j));	//计算并输出杨辉三角形	printf("\n");}return 0;
}

运行结果:
在这里插入图片描述

案例十二:最大公约数

//代码演示:
int gcd(int a, int b)
{int t = 0;if (a < b){t = a;a = b;b = t;}if (b == 0)return a;return gcd(b, a % b);
}
int main()
{int a = 0;int b = 0;scanf("%d%d", &a, &b);printf("%d\n", gcd(a, b));return 0;
}

运行结果:
在这里插入图片描述

案例十四:汉偌塔

汉诺塔问题就是将A柱上n个圆全部移动到C上,过程中可以借助B柱,但要始终保持小圆在大圆上面
在这里插入图片描述

对于n阶汉诺塔的移动次数:

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<math.h>
int main()
{int num = 0;scanf("%d", &num);//塔数printf("完成%d层的汉诺塔需要%d步\n", num, (int)pow(2,num) - 1);return 0;
}

运行结果:
在这里插入图片描述

分析:

步骤1所含步数就是n-1个圆盘移动所需的次数,我们可以将其步数看做f(n-1)。
步骤2所含步数为1。
步骤3所含步数与步骤1相似,我们也将其步数看做f(n-1)。
再观察表格中汉诺塔的移动次数,对于一阶汉诺塔移动次数就为1,对于其他的阶数则为前一阶汉诺塔移动次数 + 1 + 前一阶汉诺塔移动次数。

不难得出递推表达式:f(n-1) + 1 + f(n-1) = 2 * f(n - 1) + 1

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int Hanio_twice(int num)
{if(1 == num)return 1;elsereturn 2 * Hanio_twice(num - 1) + 1;
}
int main()
{int num = 0;	scanf("%d", &num);//塔数int ret = Hanio_twice(num);printf("完成%d层的汉诺塔需要%d步\n", num, ret);return 0;}

运行结果:
在这里插入图片描述

分析:

我们观察移动步骤,发现只有一个圆盘时移动步骤为A->C;两个圆盘时,为A->B,A->C,B->C。
那么对于n阶汉诺塔呢,我们对其进行推演:
1.把n-1个圆盘从A移动到B
2.把第n个圆盘从A移动到C
3.把n-1个圆盘从B移动到C
那n-1个圆盘如何从A移动到B呢?
1.把n-2个圆盘从A移动到C
2.把第n-1个圆盘从A移动到B
3.把n-2个圆盘从C移动到B
同样的,对于把n-1个圆盘从B移动到C,也可以推测出来:
1.把n-2个圆盘从B移动到A
2.把第n-1个圆盘从B移动到C
3.把n-2个圆盘从A移动到C
通过这些推演我们发现,汉诺塔的移动可以通过递归展开,那么以上推演步骤,我们可以将其作为递归的步骤。

思路:定义A,B,C三个字符,表示A,B,C三柱,定义n为阶数,那么n-1也就是移动步骤中,需要移动的圆盘数。
对于一阶汉诺塔,直接移动即可,对于其他的阶数,则需要通过递归展开,为n阶汉诺塔的移动步骤。在这里插入图片描述
在这里插入图片描述

//代码演示:
void move(char pos1, char pos2)
{printf(" %c -> %c \n", pos1, pos2);
}
//pos1起始位置
//pos2中转位置
//pos3目标位置
void Hannoi(int n, char pos1, char pos2, char pos3)
{if (n == 1){move(pos1, pos3);}else{Hannoi(n - 1, pos1, pos3, pos2);move(pos1, pos3);Hannoi(n - 1, pos2, pos1, pos3);}
}
int main()
{/*Hannoi(1, 'A', 'B', 'C');*///Hannoi(2, 'A', 'B', 'C');Hannoi(3, 'A', 'B', 'C');return 0;
}

运行结果:
在这里插入图片描述

2.递归与迭代

听过上面函数递归案例发现有问题,如下:

在使用 Fib 这个函数的时候如果我们要计算第50个斐波那契数字的时候特别耗费时间。
使用 Fac 函数求10000的阶乘(不考虑结果的正确性),程序会崩溃。

为什么呢?

我们发现 Fib 函数在调用的过程中很多计算其实在一直重复。

那我们如何改进呢?

在调试 Fac 函数的时候,如果你的参数比较大,那就会报错: **stack overflow(栈溢出)**这样的信息。
系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出。

那如何解决上述的问题:

将递归改写成非递归。
使用static对象替代 nonstatic 局部对象。在递归函数设计中,可以使用 static 对象替代nonstatic 局部对象(即栈对象),这不仅可以减少每次递归调用和返回时产生和释放 nonstatic 对象的开销,而且 static 对象还可以保存递归调用的中间状态,并且可为各个调用层所访问。

比如,下面代码就采用了,非递归的方式来实现:

n的阶乘

int Fac(int n)
{int i = 0;int r = 1;for (i = 1; i <= n; i++){r = r * i;}return r;
}
int main()
{int n = 0;scanf("%d", &n);int r = Fac(n);printf("%d\n", r);return 0;
}

求第n个斐波那契数

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);//20int ret = Fib(n);printf("%d\n", ret);return 0;

3.何时使用递归

如果使用递归很容易想到,写出的代码没有明显的缺陷,那我们就可以使用递归
但如果写出的递归代码,有明显问题,比如:栈溢出,效率低下等,那我们还是使用迭代的方式来解决.

💘本次专题已结束,不久将来会有更多专题与大家见面!!!

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

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

相关文章

服务器遭受攻击之后的常见思路

哈喽大家好&#xff0c;我是咸鱼 不知道大家有没有看过这么一部电影&#xff1a; 这部电影讲述了男主是一个电脑极客&#xff0c;在计算机方面有着不可思议的天赋&#xff0c;男主所在的黑客组织凭借着超高的黑客技术去入侵各种国家机构的系统&#xff0c;并引起了德国秘密警察…

Mac如何打开隐藏文件中Redis的配置文件redis.conf

Redis下载(通过⬇️博客下载的Redis默认路径为&#xff1a;/usr/local/etc) Redis下载 1.打开终端进入/usr文件夹 cd /usr 2.打开/local/文件夹 open local 3.找到redis.conf并打开,即可修改配置信息

讯飞星火认知大模型全新升级,全新版本、多模交互—测评结果超预期

写在前面 版本新功能 1 体验介绍 登录注册 申请体验 2 具体使用 2.1 多模态能力 2.1.1 多模理解 2.1.2 视觉问答 2.1.3 多模生成 2.2 代码能力 2.2.1 代码生成 2.2.2 代码解释 2.2.3 代码纠错 2.2.4 单元测试 2.3 插件功能 2.3.1 PPT生成 2.3.2 简历生成 2.3.4 文档问答 3 其他…

Android学习之路(3) 布局

线性布局LinearLayout 前几个小节的例程中&#xff0c;XML文件用到了LinearLayout布局&#xff0c;它的学名为线性布局。顾名思义&#xff0c;线性布局 像是用一根线把它的内部视图串起来&#xff0c;故而内部视图之间的排列顺序是固定的&#xff0c;要么从左到右排列&#xf…

Android之版本号、版本别名、API等级对应关系(全)(一百六十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

HTML详解连载(4)

HTML详解连载&#xff08;4&#xff09; 专栏链接 [link](http://t.csdn.cn/xF0H3)下面进行专栏介绍 开始喽CSS定义书写位置示例注意 CSS引入方式内部样式表&#xff1a;学习使用 外部演示表&#xff1a;开发使用代码示例行内样式代码示例 选择器作用基础选择器标签选择器举例特…

RISC-V公测平台发布 · 7-zip 测试

简介 7-Zip 是一个开源的压缩和解压缩工具&#xff0c;具有高压缩比和快速解压缩的特点。除了普通的文件压缩和解压缩功能之外&#xff0c;7-Zip 还提供了基准测试功能&#xff0c;通过压缩和解压缩大型文件来评估系统的处理能力和性能。 7-Zip 提供了一种在不同压缩级别和多…

BUUCTF [MRCTF2020]Ezpop解题思路

题目代码 Welcome to index.php <?php //flag is in flag.php //WTF IS THIS? //Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95 //And Crack It! class Modifier {protected $var;publi…

运维监控学习笔记7

Zabbix的安装&#xff1a; 1、基础环境准备&#xff1a; 安装zabbix的yum源&#xff0c;阿里的yum源提供了zabbix3.0。 rpm -ivh http://mirrors.aliyun.com/zabbix/zabbix/3.0/rhel/7/x86_64/zabbix-release-3.0-1.el7.noarch.rpm 这个文件就是生成了一个zabbix.repo 2、安…

流程挖掘in汽车丨宝马的流程效能提升实例

汽车行业在未来10年里&#xff0c;可能会面临比过去50年更多的变化。电动化、智能化、共享化和自动驾驶等方面的趋势可能给企业流程带来以下挑战&#xff1a; 供应链管理-电动化和智能化的发展可能导致供应链中的零部件和系统结构发生变化&#xff0c;企业需要重新评估和优化供…

zookeeperAPI操作与写数据原理

要执行API操作需要在idea中创建maven项目 &#xff08;改成自己的阿里仓库&#xff09;导入特定依赖 添加日志文件 上边操作做成后就可以进行一些API的实现了 目录 导入maven依赖&#xff1a; 创建日志文件&#xff1a; 创建API客户端&#xff1a; &#xff08;1&#xff09…

Springboot 实践(5)springboot添加资源访问目录及目录测试

前文讲解了swagger测试服务控制器&#xff0c;实现了数据库数据访问&#xff0c;这些功能都是运行在后台服务器上&#xff0c;实际用户并不能直接调用接口获取数据&#xff0c;即使用户能够利用接口获取到数据&#xff0c;数据也是结构化数据&#xff0c;不能争取转化成用户使用…

基于OFDM+64QAM系统的载波同步matlab仿真,输出误码率,星座图,鉴相器,锁相环频率响应以及NCO等

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 2.1 OFDM原理 2.2 64QAM调制 2.3 载波同步 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ............................................…

NestJs 中使用 mongoose

在 NestJS 中链接 MongoDB 有两种方法。一种方法就是使用TypeORM来进行连接&#xff0c;另外一种方法就是使用Mongoose。 此笔记主要是记录使用Mongoose的。所以我们先安装所需的依赖&#xff1a; npm i nestjs/mongoose mongoose安装完成后&#xff0c;需要在AppModule中引入…

SpringBoot后端服务开启Https协议提供访问(使用阿里云资源)

目录 概述 申请/下载证书 部署证书 本地测试访问 服务器部署访问 最后/扩展 总结 概述 本篇博客说明如何将SpringBoot项目开启Https协议提供访问。 博文以步骤【申请/下载证书】&#xff0c;【部署证书】&#xff0c;【本地测试访问】&#xff0c;【服务器部署访问】 &a…

LORA开发板采集温湿度数据,连接PC上位机显示和液晶屏显示

一、准备材料 准备以下板子和器件 Lora开发板x2 USB数据线x2 OLED 屏幕x2 StLink下载器x1 母对母杜邦线x3 DHT11 x2 二、设备连接 如图所示先将OLED 屏幕插入到开发板中 接着按照图中所示的&#xff0c;将串口一以及lora的拨码开关拨到指定方向 接着将USB数据线一端插入到…

(七)Unity VR项目升级至Vision Pro需要做的工作

Vision Pro 概述 定位为混合现实眼镜&#xff0c;对AR支持更友好 无手柄&#xff0c;支持手&#xff08;手势&#xff09;、眼&#xff08;注视&#xff09;、语音交互 支持空间音频&#xff0c;相比立体声、环绕声更有沉浸感和空间感 支持VR/AR应用&#xff0c;支持多种应用模…

FPGA应用学习笔记-----复位电路(二)和小结

不可复位触发器若和可复位触发器混合写的话&#xff0c;不可复位触发器是由可复位触发器馈电的。 不应该出现的复位&#xff0c;因为延时导致了冒险&#xff0c;异步复位存在静态冒险 附加素隐含项&#xff0c;利用数电方法&#xff0c;消除静态冒险 这样多时钟区域还是算异步的…

深度学习实战基础案例——卷积神经网络(CNN)基于SqueezeNet的眼疾识别|第1例

文章目录 前言一、数据准备1.1 数据集介绍1.2 数据集文件结构 二、项目实战2.1 数据标签划分2.2 数据预处理2.3 构建模型2.4 开始训练2.5 结果可视化 三、数据集个体预测 前言 SqueezeNet是一种轻量且高效的CNN模型&#xff0c;它参数比AlexNet少50倍&#xff0c;但模型性能&a…

Linkedin为什么要退出中国市场?

在迅速发展的时代,职场也在不断变换,只有不断地提升专业技能和进行培训,才能在职场中获得成功。Linkedin作为一家专注于职业发展的平台,专业的学习体验以及热门技能赢得了人们青睐。然而遗憾的是这个曾经让人备受青睐的平台,如今却在中国市场中黯然落幕,究竟是何种原因让曾经风…