首页 > 其他分享 >C语言的函数递归

C语言的函数递归

时间:2024-07-27 19:27:30浏览次数:9  
标签:10 函数 递归 递归函数 int C语言 main

一、递归的意义

所谓函数递归,就是在某个函数中再次调用这个函数本身,做到函数自己调用自己,这个就是函数的递归。而函数的递归主要是的作用是将一个本身比较复杂,并且步骤繁多的函数逐次的递归使其变得简单化,就比如剥笋:我们想要得到里面能吃的部分,就需要剥笋。而笋的皮有很多层,每剥开一层皮就进入新的一层,直到剥皮这个动作结束。函数递归一次就好比笋剥了一层皮,多次递归,自然就吃到里面的“笋”了。

二、递归的使用条件

通过将大问题变成稍微小一点的问题,然后再把稍微小一点的问题变得更小,直到能够直接解决,再依次从小问题到大问题逐个解决。

光靠说的可能比较难理解,写个最最简单的递归函数帮大家理解一下吧。

#include <stdio.h>
int main()
{
	printf("HelloWorld\n");
	main();
	return 0;
}

在这段代码中就是在main函数中调用了它本身,但仔细看来会发现,这段代码只管不断递归main函数,好像并没有出口呀?我们运行代码果然是输出了非常多的HelloWorld。但我们没明明没有给递归函数制造出口,明明是死循环的代码怎么自己就不接着打印了呢?这里我们能看见在打印一部分HelloWorld之后编译器就报错提醒了。其实这是因为我们每次递归一个函数,这个函数再次被调用,计算机就会为这个函数分配新的空间,但是计算机为一个进程所开辟的栈空间是一定的,当一个.cpp文件中的栈空间大于计算机为该进程所开发的栈空间时,就会报堆栈溢出错误。 因为调用函数会为函数开辟新的栈空间,也就证明,当被调用的函数返回时,调用函数中的变量会保持之前的值,因此才能够实现函数的反向输出。 

所以我们在使用递归函数的时候,是必须为递归函数创造出一个能够让递归结束的条件的,并且每一次进行函数的递归,都要使函数朝着递归结束的条件更进一步。

我们对这段代码进行一点改造,让它不再陷入死循环造成栈溢出。

#include <stdio.h>
int i;//全局变量未初始化时默认为0
int main()
{
	printf("helloworld\n");
	i++;//每次调用都会让递归函数向结束条件更近一步
	if (i == 10)
		return 0;//递归函数的结束条件
	main();
	return 0;
}

运行结果:

三、递归的优点和缺点

1.递归函数的优点

递归函数可以用于很多地方,并且通过递归函数能省去很多步骤,使原本繁琐的问题变得简单易懂,并且代码也要短上许多。就比如让我们来做这道题:输入一个整数,依次打印它的每一位 如果不去用递归函数,用常规的解题方法来思考一下该如何求解:首先我们需要先利用先/10再%10的方法,获取这个整数的每一位数字。

#include <stdio.h>
void Print(int a)
{
	int m = 0;
	int i = 0;
	int j = 0;
	int arr[20];
	while (a)
	{
		m = a % 10;
		a = a / 10;
		arr[i] = m;
		i++;
		j++;
	}
	for (i = j-1; i >= 0; i--)
	{
		printf("%d ",arr[i]);
	}
}
int main()
{
	int a;
	scanf("%d", &a);
	Print(a);
	return 0;
}

常规思想解题有一个问题:我们获取每一位数字后,得到的是最低位到最高位呀,可我们要打印的是最高位到最低位,那此时我们可能还需要再创建一个循环来颠倒打印的顺序,由此看来常规思路解题确实要麻烦。

那么接下来我们用递归的思想来看看这题如何解答:

因为递归函数是可以多次进行,然后反向打印的。那我们可以让函数一直/10,直到变成个位数,再把递归函数存储的数据依次打印出来

#include <stdio.h>
void Print(int a)
{
	if (a > 9)
	{
		Print(a / 10);
	}
	printf("%d ", a % 10);
}
int main()
{
	int a;
	scanf("%d", &a);
	Print(a);
	return 0;
}

这就是递归函数的优点,它不仅能在一定程度上简化代码,并且能将问题也简单化,更便于解决问题。

2.递归函数的缺点

虽然递归函数能使很多问题简化,代码简化,但也是一把双刃剑。

  • 递归函数的解题方法一般比较难想。
  • 递归函数多次调用函数会使用更多的栈空间,有时会降低运算的效率。
  • 如果需要运算的数据循环次数过多,容易导致堆栈溢出错误。

我们来做一道例题,直观的感受递归函数的缺点,和有些情况下它会一定程度上对程序员思想禁锢(因为敲起来比较简单,有些人做什么题都想着用递归函数)

问题:求第n个斐波那契数

斐波那契数就是像1 1 2 3 5 8 13 21 34 55...这样的数列,第n个数等于n-1个数加第n-2个数。学习过递归函数,读完题后觉得求第n个数只需要一个一个往前递归就好了呀,我们下意识的就想用递归函数去解题。那我们来打代码看看。

#include <stdio.h>
int Print(int a)
{
	if (a <= 2)
		return 1;
	else
		return Print(a - 1) + Print(a - 2);
}
int main()
{
	int a;
	scanf("%d", &a);
	int m = Print(a);
	printf("%d", m);
	return 0;
}

这样看起来确实没有错误,并且非常的简单,但是当我们输入一个比较大的数字时,比如输入数字50程序怎么不再运行了呢?其实一直在运行,但是出现了很多重复的计算,这就导致了虽然算法简单,但实则隐藏了很多冗余的算法,导致大大降低了程序的运行速度。

四、递归的练习(汉诺塔问题)

汉诺塔的游戏规则:

1、有三根相邻的柱子,标号为A,B,C。

2、A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘。

3、你需要把所有盘子移动到柱子C,并且必须还是金字塔状。

如果只有一个圆盘,只需要移动一次就好了。A->C(1步)

如果有两个圆盘,就需要借助第二个柱子(解题的关键),先把小的存放在第二个,把大的放第三个,再把小的放第一个。A->B  A->C  B->C(3步)

如果有三个圆盘,就比较麻烦了,

一共需要移动:A->C  A->B  C->B  A->C  B->A  B->C  A->C(7步)

(次数是2的n次方-1)

虽然越来越复杂,但都是一个思想:先把除了最大的盘子,都放到第二个柱,然后把最大的盘子放到第三个柱,再把除了第二大的盘子,其余的都放在第一个柱,把第二大的盘放第三个柱,依次循环往复。发现了吧!这也是一个递归函数!

#include <stdio.h>
int i;
void move(char ta1, char ta3)
{
    printf("%c->%c ", ta1, ta3);//用于移动最后剩下的一个盘子
}
int Hanoi(char ta1, char ta2, char ta3, int n)
{
    i++;//计算移动了多少次
    if (n == 1)
    {
        move(ta1, ta3);
    }
    else
    {
        Hanoi(ta1, ta3, ta2, n - 1);//先将除最下面的盘子借助第三个塔移到第二个塔
        move(ta1, ta3);//移动最后一个盘子去第三个塔
        Hanoi(ta2, ta1, ta3, n - 1);//将第二个塔上剩下的盘子借助第一个移到第三个
    }
    return i;
}
int main()
{
    int a;
    char A = 'A';
    char B = 'B';
    char C = 'C';
    scanf("%d", &a);
    Hanoi(A, B, C, a);
    printf("\n%d", i);
    return 0;
}

运行结果展示:我们用i当作计数器,发现五个盘子正好是31,符合2的n次方-1,那么汉诺塔问题就解决啦!

关于递归函数相关的知识,今天就先分享到这里啦,我们下次再见!!!

标签:10,函数,递归,递归函数,int,C语言,main
From: https://blog.csdn.net/ixiaotang_/article/details/140733840

相关文章

  • Python 可变长参数的魔法:灵活函数设计的秘密
    哈喽,大家好,我是木头左!什么是可变长参数?在Python中,可变长参数允许你向函数传入任意数量的参数,而无需预先定义它们的个数。这为编写更加灵活和通用的函数提供了可能。可变长参数主要有两种形式:*args用于非关键字参数,**kwargs用于关键字参数。*args:非关键字可变长参数当你......
  • 我在百科荣创企业实践——简易函数信号发生器(6)
            对于高职教师来说,必不可少的一个任务就是参加企业实践。这个暑假,本人也没闲着,报名参加了上海市电子信息类教师企业实践。7月8日到13日,有幸来到美丽的泉城济南,远离了上海的酷暑,走进了百科荣创科技发展有限公司。在这短短的一周时间里,我结合自己的教学经验和企业......
  • 入门C语言Day18——break&continue&goto语句
    前面的博文中有提到do-while与for循环语句,其中的流程图中有break和continue这两个部分还没解释。所以今天先来解释一下break与continue语句。break和continue两个关键字都被运用在循环中。break的作用是永久的终止循环,只要break被执行,直接就会跳出循环,继续往后执行。......
  • 写一个函数返回参数二进制中1的个数(c语言)
    1.int一共有32位,我们要知道一个数取余2等于1(n%2==1),就可以得到二进制中1的个数.然后一个数除于2(n=n/2),就可以使32位向右一位(例如:5为101,5/2==2,2为10,)。方法1(不可以输入负数)//写一个函数返回参数二进制中1的个数//方法1intcount(intn,intz){ //当a为正数的 if(n>......
  • 调整奇数全部位于偶数前面(c语言)
    1.方法一:第一步先建一个arr,判断arr中的奇数(arr[i]%2!=0)和偶数(arr[i]%2==0)分别打印,先打印奇数,后打印偶数。//调整奇数全部位于偶数前面//方法一voidtest(int*arr,intsz){ inti=0; for(i=0;i<sz-1;i++) { if(arr[i]%2!=0) { printf("%d......
  • 关于函数式弹窗组件遇到的问题
    好久没写博客了。其实也不是没写,只是没发布到的网上。这回记录一个在开发函数式组件中碰到的问题。开门还是不要见山起因是这样的,入职现在这家公司后,发现有些代码习惯挺不适合我的,就是不习惯,没事那就折腾嘛。所以就有了接下来的故事。废两句话,首先说碰到什么问题呢?公司组件......
  • 六、函数
    6.1定义函数defgreet_user():"""显示简单的问候语"""print('Hello!')greet_user()#调用函数 6.1.1向函数传递信息defgreet_user(username):"“显示简单的问候语"” print(f"Hello,{username.title()}!")greet_user('j......
  • ast获取指定python文件中携带指定装饰器函数的实现
    在实现自动化测试过程中,需要根据指定的装饰器来标记需要执行的用例或函数,下面根据使用ast库来实现读取指定文件中的数据结构后对其内容进行解析并拿到携带对应装饰器的函数。根据以下方法仅能解析func、class-func的数据结构,其余数据结构可能不兼容,需要根据实际情况进行完善调整......
  • 简单的扫雷——基于C语言的控制台小游戏
    前言:  “将大象装进冰箱要几步?--打开冰箱,把大象放进去,关上冰箱。”  同样的,该扫雷游戏的编写过程也只需三步:逻辑梳理-代码实现-运行调试。本文将使用C语言来一步步剖析并完成扫雷这一案例。一.扫雷的游戏逻辑  该扫雷的游戏逻辑为:  1.生成棋盘,并布置数个......
  • 有参构造函数注入底层源码深入剖析**前戏
    有参构造函数注入底层源码深入剖析前戏方式一:创建两个类:publicclassTestDIBean{ publicStringsay(){ return"IamTestDIBean.say()"; }}packagecom.coding.spring.practies;publicclassTestDIBean1{ privateTestDIBeantestDIBean; publicTestDIBean......