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

函数递归

    • 前言
      • 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.rhkb.cn/news/92692.html

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

相关文章

使用巴特沃兹滤波器的1D零相位频率滤波研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Python源码05:使用Pyecharts画词云图图

**Pyecharts是一个用于生成 Echarts 图表的 Python 库。Echarts 是一个基于 JavaScript 的数据可视化库&#xff0c;提供了丰富的图表类型和交互功能。**通过 Pyecharts&#xff0c;你可以使用 Python 代码生成各种类型的 Echarts 图表&#xff0c;例如折线图、柱状图、饼图、散…

AI如何看待能力,学历,文凭

1 假设&#xff1a; {文凭}⊂{学历}⊂{能力} 2 证明&#xff1a; 首先&#xff0c;我们需要明确这些集合的定义和关系。 {能力}是一个包含各种能力的集合&#xff0c;例如学习能力、沟通能力、创新能力、领导能力和专业技能等。 {学历}是一个包含各种学历的集合&#xff0c…

没学C++,如何从C语言丝滑过度到python【python基础万字详解】

大家好&#xff0c;我是纪宁。 文章将从C语言出发&#xff0c;深入介绍python的基础知识&#xff0c;也包括很多python的新增知识点详解。 文章目录 1.python的输入输出&#xff0c;重新认识 hello world&#xff0c;重回那个激情燃烧的岁月1.1 输出函数print的规则1.2 输入函…

【Java从0到1学习】09 正则表达式

1. 正则表达式概述 在编写处理字符串的程序或网页时&#xff0c;经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说&#xff0c;正则表达式就是记录文本规则的代码。 正则表达式&#xff0c;又称正规表示法、常规表示法&#xff…

机器学习笔记:李宏毅diffusion model

1 概念原理 首先sample 一个都是噪声的vector然后经过denoise network 过滤一些杂质接着继续不断denoise&#xff0c;直到最后出来一张清晰图片 【类似于做雕塑&#xff0c;一开始只是一块石头&#xff08;噪声很杂的雕塑&#xff09;&#xff0c;慢慢雕刻出想要的花纹】 同一个…

飞天使-jenkins进行远程linux机器修改某个文件的思路

文章目录 jenkins配置的方式jenkins中执行shell的思路 jenkins配置的方式 jenkins中执行shell的思路 下面的脚本别照抄&#xff0c;只是一个思路 ipall"$ips"# 将文本参数按行输出为变量 while IFS read -r line; doecho "$line" if [[ ! -z $line ]] &…

Android CameraX适配Android13的踩坑之路

AndroidCameraX适配Android13的踩坑之路 前言&#xff1a; 最近把AGP插件升级到8.1.0&#xff0c;新建项目的时候目标版本和编译版本都是33&#xff0c;发现之前的demo使用Camerax拍照和录像都失败了&#xff0c;于是查看了一下官网和各种资料&#xff0c;找到了Android13的适…

PHP实践:分布式场景下的Session共享解决方案实现

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责…

LVS的负载均衡集群

基于四层协议进行 什么是集群 含义: 1、cluster 集群、群集 2、多台主机构成&#xff0c;但对外之表现为一个整体只提供一个访问入口&#xff08;域名和地址&#xff09;相当于一台大型计算机 目前互联网应用中&#xff0c;随着站点对硬件性能、响应速度、服务稳定性、数据…

使用 `tailwindcss-patch@2` 来提取你的类名吧

使用 tailwindcss-patch2 来提取你的类名吧 使用 tailwindcss-patch2 来提取你的类名吧 安装使用方式 命令行 Cli 开始提取吧 Nodejs API 的方式来使用 配置 初始化 What’s next? tailwindcss-patch 是一个 tailwindcss 生态的扩展项目。也是 tailwindcss-mangle 项目重要…

《起风了》C++源代码

使用方法 Visual Studio、Dev-C、Visual Studio Code等C/C创建一个 .cpp 文件&#xff0c;直接粘贴赋值即可。 #include <iostream> #include <Windows.h> #pragma comment(lib,"winmm.lib") using namespace std; enum Scale {Rest 0, C8 108, B7 …

【Linux】以太网协议——数据链路层

链路层解决的问题 IP拥有将数据跨网络从一台主机送到另一台主机的能力&#xff0c;但IP并不能保证每次都能够将数据可靠的送到对端主机&#xff0c;因此IP需要上层TCP为其提供可靠性保证&#xff0c;比如数据丢包后TCP可以让IP重新发送数据&#xff0c;最终在TCP提供的可靠性机…

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

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

【Java】BF算法(串模式匹配算法)

☀️ 什么是BF算法 BF算法&#xff0c;即暴力算法&#xff0c;是普通的模式匹配算法&#xff0c;BF算法的思想就是将目标串S的第一个与模式串T的第一个字符串进行匹配&#xff0c;若相等&#xff0c;则继续比较S的第二个字符和T的第二个字符&#xff1b;若不相等&#xff0c;则…

迭代器模式-遍历聚合对象中的元素

在开发中&#xff0c;我们经常使用到Iterator这个接口&#xff0c;我们很疑惑于这个接口的作用&#xff0c;认为集合已经实现了数据访问的方法&#xff0c;增加Iterator的意义在哪。本文我们将学习迭代器模式&#xff0c;用以探讨Iterator的作用。 1.1 迭代器模式概述 提供一…

LeetCode 160.相交链表

文章目录 &#x1f4a1;题目分析&#x1f4a1;解题思路&#x1f6a9;步骤一&#xff1a;找尾节点&#x1f6a9;步骤二&#xff1a;判断尾节点是否相等&#x1f6a9;步骤三&#xff1a;找交点&#x1f344;思路1&#x1f344;思路2 &#x1f514;接口源码 题目链接&#x1f449;…

LAXCUS分布式操作系统:技术创新引领高性能计算与人工智能新时代

随着科技的飞速发展&#xff0c;高性能计算、并行计算、分布式计算、大数据、人工智能等技术在各个领域得到了广泛应用。在这个过程中&#xff0c;LAXCUS分布式操作系统以其卓越的技术创新和强大的性能表现&#xff0c;成为了业界的佼佼者。本文将围绕LAXCUS分布式操作系统的技…

Jmeter 参数化的几种方法

目录 配置元件-用户自定义变量 前置处理器-用户参数 配置元件-CSV Data Set Config Tools-函数助手 配置元件-用户自定义变量 可在测试计划、线程组、HTTP请求下创建用户定义的变量 全局变量&#xff0c;可以跨线程组调用 jmeter执行的时候&#xff0c;只获取一次&#xff0…

LeetCode-101. 对称二叉树

Problem: 101. 对称二叉树 文章目录 思路解题方法Code结果 思路 看到这个题&#xff0c;想到的解题方法是使用递归实现。判断二叉树是否对称&#xff0c;需要判断根节点的左子树和右子树是否对称。所以从根节点开始&#xff0c;递归判断左子树的左节点是否和右子树的右节点是否…