C语言:函数递归详解(建议收藏)

这篇具有很好参考价值的文章主要介绍了C语言:函数递归详解(建议收藏)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.基础概念


1.1函数递归的定义

程序调用自身的编程技巧称为递归( recursion)。
递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接
调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解

1.2函数递归的优缺点

优点:函数递归只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的主要思考方式在于:把大事化小(这种思考方式十分重要)。
缺点:①如果函数递归使用不恰当,会导致栈溢出,因为每一次函数调用都会在栈区上申请内存空间。②每一次函数递归(函数调用)都会在函数栈帧上开辟一块空间,所谓的压栈。这样会大大降低我们代码的执行效率(这会在函数递归例题详解:斐波那系数中解释)。

1.3函数递归的两个必要条件

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

二. 入门级函数递归例题


2.1函数递归之死循环

我们了解了函数递归的基础概念后,来看看这段有趣而危险的代码。

#include <stdio.h>
int main()
{
	printf("cc\n");
	main();   //重复调用main函数
	return 0;
}

可想而知,程序最终会崩溃。因为每一次函数调用都会在栈上开辟一块空间,这种死循环的代码会一直开辟空间,直至栈溢出,正如上面的缺点②。

2.2输入输出1234

题目描述:

接受一个整型值(无符号),按照顺序打印它的每一位。
例如:
输入:1234,输出 1234

解题思路:这种输入输出数字的题,我们一定要想到取模和取余的方法,并且要有限制条件,每次函数递归后,都会越来越接近这个值。
所以先函数递推1234%10=4,123%10=3,12%10=2,1%10=1,给定限制条件n>9,直到n=1,打印出最后值(1),最后函数回归打印出1234

设n为1234
print(1234/10) + 1234%10 (=4)
print(123/10) + 123%10(=3
print(12/10) + 12%10(=2
当n最后为1时,不满足我们给定的限制条件n>9时,即打印1%10(=1

代码实现

void print(unsigned int n) 
{
	if (n > 9)     //限定条件
	{
		print(n / 10);  //取模
	}
	printf("%d ", n % 10);  //取余
}
int main()
{
	unsigned int n = 0;
	scanf("%u",&n);
	//按顺序打印1234
	print(n);
	return 0;
}

递归函数c语言,C语言,c语言,算法,开发语言

三. 函数递归典型例题的实现


3.1求n的阶乘

题目描述:

用递归的方法求n的阶乘(不考虑溢出问题)
例如:
输入:4,输出 24

解题思路:n的阶乘为1234(n-1)n,我们可以先用递推的思想,先算出n(n-1)的值,再用n(n-1)的值乘以(n-2),这样依次乘下去,以n=1为限制条件,返回1。然后再用回归思想,返回回去,及可得到n的阶乘。

JC(n)
n * JC(n-1)
n * JC(n-1) * JC(n-2)
n * JC(n-1) * JC(n-2) * JC(n-3)

n * JC(n-1) * JC(n-2) * JC(n-3)…JC(1)
当满足我们的限制条件n=1时,返回1,然后回归

代码实现

int JC (int n)
{ 
	if (n == 1) 
		return 1;
	else
		return n * JC(n - 1);         //阶乘的递归实现方式        
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = JC(n);
	printf("n的阶乘为:%d", ret);
	return 0;
}

递归函数c语言,C语言,c语言,算法,开发语言

3.2strlen函数的模拟实现

题目描述:

用递归的方法模拟实现strlen函数
例如:
输入:abc,输出 3

解题思路:strlen函数遇到’\0’才会停止,所以我们以’\0’为限制条件,我们每调用一次我们自己实现的my_strlen函数,次数就加一,直到遇到’\0’停止。

my_strlen(abc)--------------这里是指针在移动
1+my_strlen(bc)
1+my_strlen(b)
1+my_strlen(‘\0’)
当满足我们的**限制条件’\0’**时,返回0,然后回归

代码实现

int my_strlen(char* str)
{

	if (*str != '\0')
	{
		return 1 + my_strlen(str + 1);     //strlen函数的模拟实现方式
	}
	return 0;
}
int main()
{
	char arr[] = "abc";
	int ret = my_strlen(arr);
	printf("%d", ret);
	return 0;
}

递归函数c语言,C语言,c语言,算法,开发语言

3.3求n的k次幂

题目描述:

用递归的方法实现n的k次幂
例如:
输入:3,3,输出 27

解题思路以k>0和k=0为限制条件,每一次递推就乘以n,并且k都减一次1,直到不满足限定条件,然后回归,即为27。

n=3,k=3
Pow(n,3)
n * Pow(n,3-1)
n * Pow(n,2-1)
n * Pow(n,1-1)
以k>0和k=0为限制条件,当k=0时,直接返回1,然后回归

代码实现

double Pow(int n, int k)
{
	if (k > 0)
		return n * Pow(n, k - 1);  //①    
	else if (k == 0)
		return 1;
	else
		return 1.0 / Pow(n, -k); //k是负数的时候---------------可以去步骤①,因为k大于零了 
}
int main()
{
	int k = 0;
	int n = 0;
	scanf("%d %d", &n,&k);
	double ret = Pow(n, k);
	printf("%.1lf\n", ret);
	return 0;
}

递归函数c语言,C语言,c语言,算法,开发语言

3.4字符串逆序

题目描述:

用递归的方法实现字符串逆序
例如:
输入:abcdef,输出 fedcba

解题思路:这题我们要以字符串长度为限制条件,先用临时变量tmp把a存起来,然后把f赋值给a,再把f置为’\0’(便于之后用strlen函数求字符串长度),每一次递推后面都要带有把tmp赋值给’\0’。之后再用临时变量tmp把存b起来,然后把e赋值给b,再把e置为’\0’…依次递推,直到字符串长度不大于1**时,回归回去,即可得到fedcba。

递推
f b c d e \0
f e c d \0 \0
f e d \0 \0 \0
回归
f e d c \0 \0
f e d c b \0
f e d c b a

代码实现

void reverse_string(char* string)                      
{
	int len = strlen(string);
	char tmp = *string; 
	*string = *(string + len - 1);
	*(string + len - 1) = '\0';
	if(strlen(string+1) > 1 )           
		reverse_string(string + 1);
	*(string + len - 1) = tmp;             //这一步才能赋值,把tmp 赋值为'\0'
}
int main()
{
	char arr[] = "abcdef";
	reverse_string(arr);
	return 0;
}

递归函数c语言,C语言,c语言,算法,开发语言

3.5斐波那契数(递归实现和非递归实现)

3.5.1递归的实现

题目描述:

计算斐波那契数递归实现求第n个斐波那契数
例如:
输入:5 输出:5
输入:10, 输出:55
输入:2, 输出:1

解题思路:斐波那系数是前两项加起来等于后一项:1,1,2,3,5,8,13…,所以我们可以以n<=2为限制条件,当n=1或2时,返回1,然后到n=3项时就是n=1项和n=2项之和,然后依次往后推,即Fib(n)就是Fib(n-1)和Fib(n-2)之和

Fib(n)
Fib(n-1) + Fib(n-2)
Fib(n-2)+Fib(n-3) , Fib(n-3)+Fib(n-4)

一直递推下去,直至到Fib(1)和Fib(2)返回值为1,然后回归,得到第n个斐波那契数

代码实现

long Fib(int n)
{
	if (n <= 2)
		return 1;
	else
		return Fib(n-1) + Fib(n-2);   //前两项加起来等于后一项
}
int main()
{
	int n = 0;
	scanf("%d",&n);
	long ret = Fib(n);
	printf("%ld", ret);
	return 0;
}

递归函数c语言,C语言,c语言,算法,开发语言

3.5.2非递归的实现

题目描述:

计算斐波那契数递归实现求第n个斐波那契数
例如:
输入:5 输出:5
输入:10, 输出:55
输入:2, 输出:1

解题思路:也可以参考上面递归实现的思路,我们可以用三个变量相互替换来解决,n1为第一项,n2为第二项,tmp为第三项,运用while()循环,每一次循环n就减1,直到n=2,最后输出tmp。

n=5
n1=1,n2=1 ,tmp=n1+n2=2
n1=n2=1,n2=tmp=2,tmp=n1+n2=3

依次类推直到n为2停下,即可得第n个斐波那契数

代码实现

int main()
{
	int n = 0;
	scanf("%d",&n);
	int i = 0;
	int n1 = 1;
	int n2 = 1;
	long tmp = 0;
	if (n == 1)
		printf("%d", 1);
	if (n == 2)
		printf("%d", 1);
	while (n>2)
	{
		tmp = n1 + n2;    //三项互相替换
		n1 = n2;
		n2 = tmp;
		n--;
	}
	printf("%1d", tmp);
	return 0;
}

3.5.3斐波那契数的非递归的实现优于递归实现的原因

①.函数递归的原则是大事化小”,对于很多问题的求解上是很遍历,而且非常迅速。但是他也是有缺点的,世界上没有完美的“事物”,函数递归也不例外。因为每一次函数递归(函数调用)都会在函数栈帧上开辟一块空间,所谓的压栈。这样会大大降低我们代码的执行效率

②.这题用递归法实现的斐波那契数正对应了其缺点因为它的递推时会有很多分支,一个分支下面又有很多分支,每一个小分支都是函数的调用,然而还有回归,函数栈帧需要销毁,这会大大降低代码的执行效率,如果n=50,则代码执行时间都要1个多小时(这里会涉及到数据结构中时间复杂度和空间复杂度的概念),所以用递归法实现的斐波那系数其实是不实用的

③.而用非递归的方法实现,可以大大提高代码的运行效率。只是每一次循环,n1,n2,tmp会被赋值,代码执行次数大大减少,所以斐波那契数的非递归的实现优于递归实现的。

3.6经典问题之《青蛙跳台阶》

题目描述:

青蛙一次可以跳一级台阶,也可以跳两级台阶。求该青蛙跳n级台阶共有多少种跳法?
例如:
输入:5 输出:8

解题思路青蛙跳台阶的思路是和斐波那系数的思路是完全等价的,只不过有了个主人公青蛙而已。因为青蛙跳1级台阶有一种走法,跳2级台阶有两种走法,而跳3级台阶有三种走法,所以跳3级台阶的走法是前面跳1级台阶和2级台阶之和,所以依次类推青蛙跳级台阶有一种走法等于跳n-1级台阶和n-2级台阶之和,所以问题就转变为求解第n个斐波那系数了。

walk(n)
walk(n-1) + walk(n-2)
walk(n-2)+walk(n-3) , walk(n-3)+walk(n-4)

一直递推下去,直至到walk(1)和walk(2)分别返回值为1和2,然后回归,得到青蛙跳n级台阶的跳法

代码实现

long walk(int n)
{
	if (n ==1)
		return 1;
	if (n ==2)
		return 2;
	else
		return walk(n-1) + walk(n-2);   //前两项加起来等于后一项
}
int main()
{
	int n = 0;
	scanf("%d",&n);
	long ret = walk(n);
	printf("%ld", ret);
	return 0;
}

递归函数c语言,C语言,c语言,算法,开发语言
递归函数c语言,C语言,c语言,算法,开发语言

3.7经典问题之《汉诺塔问题》

题目描述:

总共有三个柱子,在一根柱子上,从下往上按照大小顺序摞着n片圆盘。我们需要按大小顺序重新摆放在另一根柱子上。并且规定,在移动过程,小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘

解题思路:我们需要把圆盘看做一个一个的整体,而且需要有大事化小的思想。假设我们有三根柱子:A,B,C(A:表示起始位置,B:表示中转站
,C:表示目标位置

  • 如果A柱有n=1个盘子,我们只要把它移动到C柱上就可以了。对应过程演示(1)
  • 如果A柱有n=2个盘子,则需先把A柱上第一个盘子放到B柱上 -> 再把A柱上第二个盘子(这时n=1)放到C柱上。对应过程演示(2)
  • 如果A柱有n=3个盘子,①.第一步:则需先把A柱上第一个盘子放到C柱上 -> 再把A柱上第二个盘子放到B柱上 -> 然后把C柱上的盘子放到B柱上面 -> 然后把A柱上第三个盘子(这时n=1)放到C柱上。②.第二步:再想办法把B柱上的圆盘移动到A柱上,先把B柱上第一个圆盘放到A柱上 -> 再把B柱上的圆盘放到C柱上 -> 最后再把A柱上的圆盘放到C柱上。对应过程演示(3)
  • 如果A柱有n个盘子,步骤是一样的,肯定是先想办法把A柱n-1个圆盘移动到B柱上 -> 之后才能想办法把第n个圆盘从A柱放到C柱上面(即n=1的时候,递归的限制条件) -> 最后想办法把B柱上的圆盘移动到C柱上面。对应过程演示(4)

这里递归的限制条件是n=1
并且一定要注意:我们在解决汉诺塔问题时,一定不能太过于深究里面圆盘移动的过程,因为比较复杂,很容易让人绕进去。所以我们这里不考虑上述中的“想办法”(即移动的过程)
我们只要懂其中的原理就可以把汉诺塔实现出来,运用大事化小的思想

代码实现

void Move(char src, char dest)
{
	// src表示的是起始位置,dest表示的是目标位置
	printf("盘子从%c柱子->%c柱子\n",src,dest);
}
void Plate_Move(int n, char A, char B, char C)
{
	if (n == 1)
	{
		Move(A, C);   //这里即递归停下来的地方,把最底下一层的盘子(n),移动到C柱上
	}
	else //这里下面都是在递归ing!!! (下面这三条语句其实都是在同步进行的)
	{
		Plate_Move(n-1,A,C,B);//当不只一个圆盘时,我们先将上面 (n -1)个圆盘 借助 C柱子  从 A 柱子移动到 B 柱子
		
		Move(A, C);      //A柱剩余一个圆盘,将剩下的一个圆盘从 A 移动到 C
		
		Plate_Move(n - 1, B, A, C);  //以A柱为中转站,把B柱上的圆盘放在C上。
	}

}
int main()
{
	int n = 0;
	scanf("%d", &n);
	Plate_Move(n, 'A', 'B', 'C');  //n为几个圆盘,A,B,C分别对应A,B,C三个柱子
	return 0;
}

过程演示

(1)A柱上有1个圆盘:

递归函数c语言,C语言,c语言,算法,开发语言
(2)A柱上有2个圆盘:

递归函数c语言,C语言,c语言,算法,开发语言
(3)A柱上有3个圆盘:

递归函数c语言,C语言,c语言,算法,开发语言
(4)A柱上有n个圆盘:

递归函数c语言,C语言,c语言,算法,开发语言文章来源地址https://www.toymoban.com/news/detail-778767.html

到了这里,关于C语言:函数递归详解(建议收藏)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 掌握Go语言:探索Go语言递归函数的高级奥秘,优化性能、实现并发、解决算法难题(28)

    递归函数在Go语言中是一种强大的工具,能够解决许多复杂的问题。除了基本的递归用法外,Go语言还提供了一些高级用法,使得递归函数更加灵活和强大。本文将深入探讨Go语言递归函数的高级用法,包括尾递归优化、并发递归和记忆化递归等。 尾递归优化 尾递归是一种特

    2024年04月10日
    浏览(53)
  • 【初阶C语言3】特别详细地介绍函数以及在初阶中重要的算法——递归

     💓作者简介: 加油,旭杏,目前大二,正在学习 C++ , 数据结构 等👀 💓作者主页:加油,旭杏的主页👀 ⏩本文收录在:再识C进阶的专栏👀 🚚代码仓库:旭日东升 1👀 🌹欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖        从标题也能看出来,我们有要进行 超详细

    2024年02月08日
    浏览(47)
  • 详解OSI七层网络协议(建议收藏)

    目录 1、层级  2、物理层 3、数据链路层 4、网络层 5、传输层

    2024年02月06日
    浏览(39)
  • Linux下编译运行C语言文件(建议收藏)

    一、准备C文件 在命令行模式下输入:vim test.c(vi也可以,但建议用vim) 进入编辑模式,输入以下代码: 首先点击ESC键退出编辑模式,然后输入: wq (注意输入的时候有冒号哦)回到命令行。   二、 编译 编译C文件成可执行文件 执行的命令:gcc test.c -o test 输入ls命令,能看到当前文

    2024年02月09日
    浏览(27)
  • 【C语言初阶】指针的详细解析(建议收藏)

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 :《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,今天给大家带来的指针篇的初阶,带你先从底层一步步理解指针!    ⛳️ 指针可以说是C语言最重要的部分了!俗话说

    2024年02月14日
    浏览(43)
  • 【algorithm】算法基础课---二分查找算法(附笔记 | 建议收藏)

    🚀write in front🚀 📝个人主页:认真写博客的夏目浅石. 🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝 📣系列专栏:AcWing算法学习笔记 💬总结:希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🖊 ✉️ 如果无聊的话,就来逛逛我的博客栈吧 stack-frame.cn 关于我

    2024年01月18日
    浏览(40)
  • 密码爆破漏洞详解——黑客必修入门操作( 建议收藏 )

    隔壁老张: “狗剩啊, 隔壁xx村的王姐家的女娃好漂亮,我想盗她qq啊, 你帮我个忙呗!” 狗剩: “我不会呀!” 村里大妈:“那个狗剩啊, 连盗个qq号都不会,他妈还好意思说他是学网络安全当黑客的” 密码爆破又叫 暴力猜解 , 简单来说就是将密码逐个尝试, 直到找出真正的密

    2024年02月07日
    浏览(59)
  • 史上最详细的八大排序详解!(建议收藏)

    🚀write in front🚀 📜所属专栏:初阶数据结构 🛰️博客主页:睿睿的博客主页 🛰️代码仓库:🎉VS2022_C语言仓库 🎡您的点赞、关注、收藏、评论,是对我最大的激励和支持!!! 关注我,关注我,关注我 , 你们将会看到更多的优质内容!!   从今天开始,我们就进入

    2023年04月20日
    浏览(51)
  • 全网最详细的线程池 ThreadPoolExecutor 详解,建议收藏!

    五种状态: 线程池的 shutdown() 方法,将线程池由 RUNNING(运行状态)转换为 SHUTDOWN状态 线程池的 shutdownNow() 方法,将线程池由RUNNING 或 SHUTDOWN 状态转换为 STOP 状态。 注: SHUTDOWN 状态 和 STOP 状态 先会转变为 TIDYING 状态,最终都会变为 TERMINATED ThreadPoolExecutor 继承自 AbstractExe

    2024年02月03日
    浏览(46)
  • GitHub新手用法详解【适合新手入门-建议收藏!!!】

    目录 什么是Github,为什么使用它? 一、GitHub账号的注册与登录 二、 gitbash安装详解 1.git bash的下载与安装 2.git常用命令  3. Git 和 GitHub 的绑定 1. 获取SSH keys  2.绑定ssh密钥 三、通过Git将代码提交到GitHub 1.克隆仓库   2.测试提交代码         GitHub是一个面向开源及私有软件项

    2023年04月24日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包