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

函数递归

    • 前言
      • 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;
	else
		return Fib(n - 1) + Fib(n - 2);
}
int main()
{
	int n = 0;
	scanf("%d", &n);//20
	int 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打印用lf
	return 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;
	else
		return 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;
	else
		return 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;
	else
		return 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);//20
	int ret = Fib(n);
	printf("%d\n", ret);
	return 0;

3.何时使用递归

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL | 排序检索的数据

3-排序检索的数据 使用order by语句排序检索到的数据。 3.1-排序数据 使用SQL语句返回一个数据表的列。 select prod_id from products; --------------------- | prod_name | --------------------- | 8 inch teddy bear | | 12 inch teddy bear | | 18 inch teddy bear |…

【java毕业设计】基于SSM+MySql的个人交友网站设计与实现(程序源码)--个人交友网站

基于SSMMySql的个人交友网站设计与实现&#xff08;程序源码毕业论文&#xff09; 大家好&#xff0c;今天给大家介绍基于SSMMySql的个人交友网站设计与实现&#xff0c;本论文只截取部分文章重点&#xff0c;文章末尾附有本毕业设计完整源码及论文的获取方式。更多毕业设计源…

自动驾驶数据集汇总

1.Nuscenes 数据集链接&#xff1a;nuScenes nuscenes数据集下有多个任务&#xff0c;涉及Detection&#xff08;2D/3D&#xff09;、Tracking、prediction、激光雷达分割、全景任务、规划控制等多个任务&#xff1b; nuScenes数据集是一个具有三维目标注释的大型自动驾驶数…

【JAVA】变量的作用域与生存周期

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️初识JAVA】 文章目录 前言变量的作用域变量的生命周期局部变量全局变量 前言 变量&#xff0c;我们学习过程中逃不掉的知识&#xff0c;无论在哪种语言中我们都需要学会去合理的运用它&#xff0c;今…

Hadoop+Python+Django+Mysql热门旅游景点数据分析系统的设计与实现(包含设计报告)

系统阐述的是使用热门旅游景点数据分析系统的设计与实现&#xff0c;对于Python、B/S结构、MySql进行了较为深入的学习与应用。主要针对系统的设计&#xff0c;描述&#xff0c;实现和分析与测试方面来表明开发的过程。开发中使用了 django框架和MySql数据库技术搭建系统的整体…

中科亿海微FIFO使用

引言 FPGA&#xff08;现场可编程门阵列&#xff09;是一种可编程逻辑器件&#xff0c;具有灵活性和可重构性&#xff0c;广泛用于数字电路设计和嵌入式系统开发。在FPGA中&#xff0c;FIFO&#xff08;First-In, First-Out&#xff09;是一种常见的存储器结构&#xff0c;用于…

大数据课程I1——Kafka的概述

文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 了解Kafka的概念; ⚪ 掌握Kafka的配置与启动; 一、简介 1. 基本概念 Apache kafka 是一个分布式数据流平台。可以从如下几个层面来理解: 1. 我们可以向Kafka发布数据以及从Kafka订阅…

cpu和io的关系

在说io的五中模型之前,先说说Io把文件从哪里移到了哪里 自己的理解: 根据工作或者遇到的业务. 文件不可能存在缓存或在内存中,因为缓存和内存不能永久性储存东西, 文件需要被永久性储存.因此文件都存在电脑的硬盘里, 或者存在云服务器的它们的硬盘里. 我们io文件, 第一…

【机器学习4】构建良好的训练数据集——数据预处理(一)处理缺失值及异常值

数据预处理 &#x1f4ab;数据预处理的重要性&#x1f4ab;处理缺失值⭐️识别表格中的数据⭐️计算每列缺失值的数量⭐️删除含有缺失值的样本或特征⭐️填充缺失值 &#x1f4ab;处理异常值⭐️异常值的鉴别⭐️异常值的处理 &#x1f4ab;将数据集划分为训练数据集和测试数据…

易服客工作室:PressMart – 现代Elementor WooCommerce WordPress商城主题

PressMart是现代且独特的 Elementor WooCommerce WordPress商城主题。它配备了高品质的 05 预建主页&#xff0c;适合任何在线商店&#xff0c;如时装店、电子产品商店、家具店等。 我们使用 Elementor – 一个拖放页面构建器&#xff0c;不需要用户的编码技能即可轻松编辑和构…

【Python】使用python解析普通格式的报文为someip格式报文

文章目录 1.安装scapy库2.示例 1.安装scapy库 使用 pip 安装 scapy 第三方库&#xff0c;打开 cmd&#xff0c;输入以下命令&#xff1a; pip install scapy出现如图所示&#xff0c;表示安装成功&#xff1a; 2.示例 要解析someip格式报文&#xff0c;需要导入someip模块&a…

Qt扫盲-Qt Model/View 理论总结 [上篇]

Qt Model/View 理论总结 [上篇] 一、概述1.model / view 架构2. Model3. View4. Delegate5. 排序6. 快捷类 二、使用model/view1. Qt包含两种 model2. 在现有 model 中使用 view 三、Model 类1. 基本概念1.model 索引2. 行和列2. item 的父 item3. Item roles4. 总结 2. 使用mo…

flutter 初识(开发体验,优缺点)

前言 最近有个跨平台桌面应用的需求&#xff0c;需要支持 windows/linux/mac 系统&#xff0c;要做个更新应用的小界面&#xff0c;主要功能就是下载更新文件并在本地进行替换&#xff0c;很简单的小功能。 花了几分钟构建没做 UI 优化的示例界面&#xff1a; 由于我们的客…

深入理解Vue 3.0x中的Suspense和异步组件

深入理解Vue 3.0x中的Suspense和异步组件 Vue 3.0x作为Vue.js框架的最新版本&#xff0c;引入了许多创新特性&#xff0c;其中Suspense和异步组件是重要的改进之一。在本文中&#xff0c;我们将深入探讨这两个特性&#xff0c;了解它们如何为现代Web应用带来更好的性能和用户体…

简单易懂的 Postman Runner 参数自增教程

目录 什么是 Postman Runner&#xff1f; Postman Runner 如何实现参数自增&#xff1f; 步骤一&#xff1a;设置全局参数 步骤二&#xff1a;将全局参数带入请求参数 步骤三&#xff1a;实现参数自增 资料获取方法 什么是 Postman Runner&#xff1f; Postman Runner 是…

【Apollo】推动创新:探索阿波罗自动驾驶的进步(含安装 Apollo的详细教程)

前言 Apollo (阿波罗)是一个开放的、完整的、安全的平台&#xff0c;将帮助汽车行业及自动驾驶领域的合作伙伴结合车辆和硬件系统&#xff0c;快速搭建一套属于自己的自动驾驶系统。 开放能力、共享资源、加速创新、持续共赢是 Apollo 开放平台的口号。百度把自己所拥有的强大、…

python selenium如何保存网站的cookie用于下次自动登录

## 一、python selenium如何保存网站的cookie 使用Selenium保存网站的Cookie非常简单。下面是一个示例&#xff0c;展示了如何使用Selenium打开网站&#xff0c;然后保存获取到的Cookie&#xff1a; from selenium import webdriver# 初始化浏览器 browser webdriver.Chrome…

Mysql复制类型、主从复制集群种类、主从复制原理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 主从复制原理主从复制原理图一、概述二、为什么要读写分离&#xff1f;三、mysql支持的复制类型1、基于语句的复制statement&#xff1a;2、基于行的复制row&#x…

JavaFx基础学习【五】:FXML布局文件使用

目录 前言 一、介绍 二、简单体验 三、FXML标签元素 四、fx属性介绍 五、重写initialize&#xff08;名字需要保持一致&#xff09;方法 六、Scene Builder快速布局 前言 如果你还没有看过前面的文章&#xff0c;可以通过以下链接快速前往学习&#xff1a; JavaFx基础学…

【Spring】深入理解 Spring 事务及其传播机制

文章目录 一、Spring 事务是什么二、Spring 中事务的实现方法2.1 Spring 编程式事务&#xff08;手动&#xff09;2.1.1 编程式事务的使用演示2.1.2 编程式事务存在的问题 2.2 Spring 声明式事务&#xff08;自动&#xff09;2.2.1 Transactional 作用范围2.2.2 Transactional …