首页 > 其他分享 >C进阶 函数的递归

C进阶 函数的递归

时间:2024-11-24 11:29:16浏览次数:10  
标签:这个 进阶 递归 int num printf 我们 函数

文章目录

一,函数递归的介绍

二,考虑仅变量的递归

三,考虑指针与地址的递归

四,迭代与递归的选择与二者之间的利与弊

五,经典题目:汉诺塔问题(感兴趣可以去看看视频学习一下,这个文章不太好描述)


前言

此类算法可以简化代码的书写量,进而可以更加简洁的书写自己的代码

一.函数递归的介绍

程序调用自身的编程技巧称之为递归

(主要思考方式:把大事化小事)它通常把一个大型复杂的问题层层转换为一个一个与问题相似规模较小的问题

(注:1.vs上面有一个快速注释的按键可以利于调试   2.%u是指输出的内容不会包含正负号,数字总是大于0)

二,考虑仅变量的递归

例题:接受一个整形的值(无符号),按照顺序打印他的每一位

我们使用正常的思路来解这个题目(迭代法)

注:迭代法就是我们平时所用的for while循环那些

#include<stdio.h>

int main()
{
	int num = 0;
	scanf("%d", &num);
	while (num) {
		printf("%d ", num % 10);
		num = num / 10;
	}
	return 0;
}

我们利用了这个迭代法来解决了这个问题(vs2019测试) 

接下来我们来用迭代法来实现这个功能

迭代法的思想:我们要把一个大型复杂的问题层层转换为一个一个与问题相似规模较小的问题

我们用这个思想,就可以把这个打印1234逐步变成一个很easy的问题,接下来我们用代码实现一下这个思想

#include<stdio.h>
void print(unsigned int num) {
	if (num > 0) {
		print(num / 10);
		printf("%d ", num%10);
	}
}
int main() {
	unsigned int num = 0;
	scanf("%d", &num);
	print(num);
	return 0;
}

这里我们用vs2019测试

 

这里是用来递归的思想,注意我们在while循环里面,这个里面第一次执行完print的时候没有立马执行printf,如果第一次while循环执行到print的时候,如果直接打印了,那么这个时候不是1在前面了,是4在前面,我们利用一个图分析

 

这样我们就实现了递归,当然如果把print和printf倒着换过来,这样就是先打印4然后再进行递归 

 这样就可以实现4 3 2 1的结果了

从这个程序我们就可以知道递归是一层一层进行像剥洋葱一样来进行实现程序的进行,我们可以巧用再递归的上面写程序还是下面写程序,这些都是可以巧用的,有助于我们来写程序(n>0这个条件十分常用)

对于此案例的递归总结

1,如果没有(n>0)这个条件的话就会报错,Stack  overflow

栈溢出的是由于无递归条件使程序进入了死循环,这样就会出现栈溢出的现象

2.如果没有%10.也会出现Stack  overflow

这样我们就可以学习到我们应该要有两个限制递归的条件

1.限制条件1:满足这个条件,递归将不再继续进行(一般用if语句)

2.限制条件2:满足这个条件,每次的递归都会越来越接近限制条件1然后来结束递归(一般放入递归函数里面,如这里的print(num%10))

这里我们在使用和这个递归的时候要考虑到他怎么在我们内存里面存活下来的,因为我们每次都是可以调用到上一次的值,上一次的值是并没有销毁的而是保存到内存里面的,所以这个递归是很容易出现Stack  overflow的,因为每一都是像剥洋葱一样剥下来的,所以值都没有销毁,当然来有Stack  overflow说明这个是存在STACK里面的,我们来看一个图来了解一下内存

那么我们该怎么将一个复杂的问题转换为一个规模较小的问题呢?

我们只需记得有来有回即可我们来看一个图来理解有来有回这个方法

这个图是根据上面的一个例子所画出来的,这样就可以更好的帮助我们来了解这个方法

对于有来有回,我们利用一个例子来加深这个记忆

#include<stdio.h>
void story(int a) 
{
	if (a > 0) {
		printf("我爱你%d\n", a);
		story(a - 1);
		printf("说了几次我爱你了,我就是不喜欢你,我有喜欢的人%d\n", a);
	}
	else
		printf("说完之后,听到了ta的话心碎了\n");
}
int main() 
{
	int a = 0;
	scanf("%d", &a);
	story(a);
}

这个代码当我们输入5的时候(用vs2019测试)

 我们来看这个输出的顺序,首先我们输出我爱你,我爱你后面的数字是倒序的说明是正常先进行执行,但是说了几次我爱你了,我就是不喜欢你,我有喜欢的人,这个后面数字是正序的,说明他是先要一直执行,后面递归结束的时候,在进行输出数字,这个说完之后,听到了ta的话心碎了这话我们就是在结束递归的时候,else先执行,因为a=0的时候也有一个结果,所以这个是比说了几次我爱你了,我就是不喜欢你,我有喜欢的人先执行的

(总结:这里的我爱你是表示来的,说了几次我爱你了,我就是不喜欢你,我有喜欢的人是表示回的,一个男生给女生表白表示来,女生的回话表示回,中间else表示他们之间的一个值,表示他们之间的一个过程ta心碎了)

三,考虑指针与地址的递归

例题:编写函数求字符串的长度

我们用迭代法(计数法):

#include<stdio.h>

int my_strlen(const char* str) {
	int count = 0;
	while(*str != '\0') {
		count++;
		*str++;
	}
	return count;
}
int main()
{
	int len = my_strlen("abc");
	printf("%d", len);
	return 0;
}

这里很简单,不懂可以自己看看,用vs2019测试

 

接下里我们用递归法来解这个问题(不允许创建临时变量) 

#include<stdio.h>

int my_strlen(const char* str) {
	if (*str != '\0') {
		return 1 + my_strlen(str + 1);
	}
	return 0;
}
int main()
{
	int len = my_strlen("abc");
	printf("%d", len);
	return 0;
}

这里我们用来递归法来实现的,这里考虑到了地址的加1,很巧妙这个实现,然后每次用1+()这个后面的值,就可以很方便的实现这个程序了,记住字符串很多都是用\0来作为限制条件的

四,迭代与递归的选择与二者之间的利与弊 

什么时候用迭代法什么时候用递归法,我们可以拿斐波那契数列来举这个例子:

我们来用递归实现一下

#include<stdio.h>
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);
	int ret = Fib(n);
	printf("%d", ret);
	return 0;
}

这个是我们用递归法来实现的,但是用vs2019测试50的时候,很慢

我们可以打开任务管理器,可以看到这个代码是一直在cpu计算的,而且电脑还会时不时卡顿一下,为什么呢?我们已40为例子,看看这个电脑是怎么计算的(我们用二叉树来看看)

这个40他分裂分裂,分了38万次,能不复杂吗?所以我们在使用递归应该考虑他的复杂程度

那么我们怎么改变上面的而让计算机计算的更快呢?

1,将递归改为非递归

2,用static静态局部变量来代替nonstatic局部变量

(减少递归的调用,同时返回产生和释放局部变量的开销,而static还可以保持递归调用的中间状态,并且可以为各个调用层所访问)

 五,经典题目:汉诺塔问题

由于这个问题有些许复杂,所以要有用到很多图,这里直接用笔来画


总结

我们学习到了递归是什么,变量递归,指针地址递归,迭代和递归的区别,什么时候使用递归,递归运算量很大该怎么修改,额外问题汉诺塔问题

标签:这个,进阶,递归,int,num,printf,我们,函数
From: https://blog.csdn.net/2401_86738532/article/details/144003452

相关文章

  • STM32 通过STM32cubemx软件进行代码生成(led灯闪烁)并最后封装点亮、熄灭以及翻转灯函数
    第一步生成代码对hal生成的文件进行解释Core:核心->Inc:各种头文件->Src:各种源文件Drivers:驱动文件MDK:可以看到个keil各种文件项目路径hail.ioc,可以用来修改配置,工作日志和配置文件 第二步点击MDK-ARM可以看到keil文件,双击打开keil文件对其配置自动复位功......
  • 机器学习实战笔记34-38:gridsearchcv的进阶使用,无监督学习:kmeans、DBSCAN
    主要讲了gridsearchcv的几种变形使用方式一:全部参数搜索方法是构造机器学习流之后,构造参数空间二:优化评估指标的选择作为网格搜索中输出评估指标的参数,roc_auc参数只能指代metrics_roc_auc_score函数的二分类功能,如果需要多分类,则需要将scoring修改为roc_auc_ovr等参数三......
  • LeetCode题解:29.两数相除【Python题解超详细,位运算、二分查找法、递归法】,知识拓展:位
    题目描述        给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.7335 将被截断至 -2 。返回被除数 dividend 除以除数 div......
  • MySQL中的ROW_NUMBER窗口函数简单了解下
    ROW_NUMBER()是MySQL8引入的窗口函数之一,它为查询结果集中的每一行分配一个唯一的顺序号(行号)。这个顺序号是基于窗口函数的ORDERBY子句进行排序的,可以根据指定的排序顺序生成连续的整数值。ROW_NUMBER()在分页、去重、分组内排序等场景中非常有用。本文涉及到的脚本测试请......
  • ybtoj 高效进阶题解索引
    密码:sunxuhetai2009--------------------云落的分割线--------------------基础算法第1章-递推算法第2章-贪心算法第3章-二分算法第4章-深搜算法第5章-广搜算法已完结--------------------云落的分割线--------------------图论第1章-并查集第4章-强连......
  • 【C/C++】main函数为什么要return 0?
    文章目录先看看AI怎么说表示程序成功退出为什么是return0不是return1呢?语法角度总结先看看AI怎么说在C语言中,main函数的return0;表示程序成功执行并正常退出。它是程序的退出状态码,通常用于告诉操作系统程序的运行状态。返回0表示程序没有发生错误并正常结......
  • 【Python图像处理】进阶实战续篇(五)
    在前几篇文章中,我们已经探讨了Python在图像处理领域的多种技术,包括图像分割、视频处理、三维重建、图像增强、面部识别、文字识别、图像检索以及医学图像处理。本篇将继续深入探讨更多图像处理技术及其应用实例,并结合更多的知识点说明,以帮助读者更全面地掌握图像处理领域的......
  • 【Vue2】利用组件递归方式实现目录树组件
    前言    看到最近一些公司前端笔试题,发现他们都很喜欢考察递归。这使我不得不想到在前端开发中,也会遇到的一些需要利用递归思想实现的一些场景,如目录树组件,大多数前端开发经常参与流水型业务,对组件递归的场景用之较少。以下为作者根据实践,分享递归组件实现目录树的设......
  • # 【鸿蒙开发】----##《鸿蒙所有的生命周期函数梳理》##
    【鸿蒙开发】----鸿蒙所有的生命周期函数梳理文章目录【鸿蒙开发】----鸿蒙所有的生命周期函数梳理什么是生命周期?一、UIAbility的生命周期二、组件的生命周期1.系统组件的生命周期函数2.自定义组件的生命周期函数3.页面组件的生命周期函数4.aboutToReuse复用组件生命周......
  • 【Solution】用C语言代码绘制线性函数包围图
    题目:绘制左边图的众多*输出图像,函数已给出:y=1,y=-x+2n,y=x。解决方案: 思路对于原来的坐标几何图形,2<=n,y<=x<=2n-y,1<=y<=x。我们用来写C代码的函数首先要确定三角形高的范围,至少要2。将图形分隔成上下两部分。从最高的顶点到三角形高的部分,和其下面的部分。使用line......