【c语言】函数递归(含青蛙跳台阶,汉诺塔问题)

函数递归

学习C语言函数一定绕不开函数递归,它能帮助我们把一些复杂且步骤 相似的问题拆成一个个相同的小步骤,通过多次调用和递归实现功能。


前言

提示:这里可以添加本文要记录的大概内容:

递归的思想

递归的思想就是把一个问题层层转化成一个小问题,解决规模小的子问题,直到子问题不能被拆分,递归的思想就是大事化小的问题
递归的递就是递推,归就是回归

递归的限制条件

递推想要结束,就必须有限制条件,让递推不会一直进行下去
因此当我们在写递推函数的时候,要满足两个条件

1.递推存在递归条件,当满足条件时,递归就不再进行
2.当每次进行递归时,都要慢慢接近这个条件

递归例子

仅仅是这些理论知识还不能理解得很好函数递归,下面学习一些递归例子慢慢理解函数递归

1.求阶乘

题目:给一个数n,求n的阶乘
分析一波
我们知道n的阶乘
n=n*(n-1) * (n-2)* …* 3* 2*1
这里给出一个举例:5!

5!=5 * 4 *3 * 2 *1
可以将它拆解成这样
F(5)=5 *F(4)
F(4)=4 *F(3)
F(3)=3 *F(2)
F(2)=2 *F(1)
F(1)=1 *F(0)
F(0)=1

当求出F(0)时,就可以返回去求上边的数的阶乘

可以看一下分析的图帮助理解

在这里插入图片描述
给出代码例子:

#include<stdio.h>
int Fact(int n)
{
	if (n == 0)
		return 1;
	else
		return n * Fact(n - 1);
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret=Fact(n);
	printf("%d\n", ret);
	return 0;
}

2.顺序打印

输入一个整数,顺序打印每一位数

例如输入:1234
输出:1 2 3 4

如果输入的数是一个1位数,这个数的每一位就是他自己
如果输出的是个多位数,就要把每一位数拆分

1234%10就可以得到4,在用1234/10,丢到4,得到123
然后再继续对123%10得到3,123/10丢到3,得到12
就这样操作下去
直到得到一个数字的每一位数

但是我们这样得到的每一位数是逆序的,我们可以通过调整一下拆分顺序来实现顺序打印

void Print(int a)
{
	if (a > 9)
		Print(a / 10);
	printf("%d ", a % 10);

	
}

在这里插入图片描述

当然,还有一种方式

void Print(int n)
{
	if (n == 0)
		return;
	Print(n / 10);//先递归处理数字,再调用返回后在输出
	printf("%d ", n % 10);
}

这样也能顺序得到每一位数字
在这里插入图片描述

如果看图还是不理解的话,可以去调试一下,一步步地看过程,就会很容易理解了

3.斐波那契

在这里插入图片描述
看这个公式,我们可以用一个简单的递归把它写出来

int Fib(n)
{
	if (n == 1 || 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;
}

拓展题目

1.青蛙跳台阶

问题:一只青蛙可以一次跳1级台阶,也可以一次条两级台阶,求青蛙跳上一个n级台阶有几种跳法
先来分析
假设一只青蛙跳n级台阶的跳法为Jump(n)种
当n=1,他的跳法只有一种,Jump(1)=1;
当n=2;他的跳法有两种,要么一次跳两级台阶,要么一级一级台阶跳,Jump(2)=2;
然而当n>2时,青蛙最后一次跳就有两种情况
1.当最后一次跳一级台阶时,前边跳了n-1级台阶,跳法数量为Jump(n-1)
2.当最后一次跳2级台阶时,前边跳了n-2级,跳法数量为Jump(n-2)
这样我们就可以得出结论

Jump(n)=Jump(n-1)+Jump(n-2)

感觉和斐波那契有点像呢?
不过,仔细看还是有点区别的。

代码示例;

int Jump(int n)
{
	if (n == 1)
		return 1;
	else if (n == 2)
		return 2;
	else
		return Jump(n - 1) + Jump(n - 2);
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = Jump(n);
	printf("%d", ret);
	return 0;
}

2.汉诺塔问题

汉诺塔(Tower of Hanoi)问题是一个经典的数学谜题和递归算法的示例。

问题描述:有三根柱子 A、B、C,在 A 柱上有 n 个圆盘,圆盘大小不等,大的在下,小的在上。要将 A 柱上的所有圆盘借助 B 柱移动到 C 柱上,在移动过程中,始终保持大盘在下,小盘在上。

移动规则:

  1. 每次只能移动一个圆盘。
  2. 圆盘只能从一根柱子的顶部移动到另一根柱子的顶部。(就是大盘不能在小盘上面)

求:
把A主上的圆盘全部移到C柱子上最少的移动次数
在这里插入图片描述
先分析一波
想要完成把这3个圆盘,移到c柱子,我们肯定是要先借助B柱子,把1号盘子以到C柱子,然后再把23号盘子移到C柱子
我们先把23看作一个整体,那么完成这个任务只需要三步
1.把23移到B柱子(中转柱)
2.把1移到C柱子(目标柱)
3.再把23移到C柱子

那么23怎末移动呢?这时候我们再把23拆开看,仍然分为三步
1.把3移到中转柱子
2.把2移到目标柱子
3.再把3移回目标柱子

这样看其实23也就相当于一个小的汉诺塔问题了
因此根据我们的分析,汉诺塔问题的3个步骤

1.把n-1个圆盘移到中转柱
2.把最底下的圆盘移到目标柱
3.把n-1个圆盘移回目标柱

若我们假设移动N个圆盘最少需要Move(n)次,那么就满足表达式

Move(n)=2*Move(n-1)+1

这里的2倍是一次是把n-1个圆盘移到中转柱子的次数,一次是把n-1个圆盘移回目标柱子的次数
1是指移动最下面那个圆盘需要移动1次

这样我们就可以写出他的递归

int Move(n)
{
	if (n == 1)
		return 1;
	else
		return 2 * Move(n - 1) + 1;
}

int main()
{
	int n = 0;//初始化盘子的个数
	scanf("%d", &n);
	int ret = Move(n);
	printf("最少移动%d次", ret);

	return 0;
}

当然我们也可以运用一些数学知识通过上边的结论求出Move(n)=2^n-1的表达式,不过我觉得求出表达式的时候就不必要在用递归了吧,直接套表达式不就行了吗?

作者有话说:作者只是一只小白,以上均是作者自己对所学知识的理解,如有错误,感谢指出,如感到有帮助,请留下一个👍

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值