无限深链---函数递归

前言

Java和C的函数递归大同小异。只此一篇足以。

最近遇到了好多递归问题,printf不能以二进制的形式打印,好吧我就用递归弄一个函数,思来想去,还是把这个部分放在这里,也算有始有终。

一、什么是函数递归


  IDE:VS2022。

  C语言中,函数可以调用自身,这个调用自身的过程就是递归,递归可以很简洁解决问题,但结束递归往往是难处。但递归太深了,内存不够用,会栈溢出。(函数栈帧部分,函数调用会在栈上开辟新空间,而递归调用自身,也会开辟。如果递归太深了或者死递归必然栈溢出程序崩溃)

二、如何理解并使用递归

递归可以逐字拆分理解,递是递推,归是回归。先递推后回归,意思是递推有终点不能再递推而开始返回值(回归)的情况。

递归思想:与数学上的化归思想类似,将大的问题分解成小的子问题然后重复操作直到不能拆分位置,由复杂到简单转换过程。

递推的条件:

  • 递推有终点。
  • 每次递归(调用自身)会越来越进接近这个限制条件即终点。(之后便从终点开始逐步往上返回值。
  • 将原问题划分成其子问题,注意:子问题必须要与原问题的解法相同。

三、递归实例

计算n的阶乘

简单回顾;数学上定义的n的阶乘为n!=1*2*3*4...*(n-1)*n,并规定0也有阶乘即0!=1;

由此可以知道1!=1;2!=1*2=2;3!=1*2*3=6等。

方法找递推公式

//设Fn=n!,n!=n*(n-1)!
//意味着Fn=n*F(n-1);递推公式就被找出来;
#include<stdio.h>
int Fn(int x);
int main()
{
	int a;
	scanf("%d", &a);
	printf("%d", Fn(a));
	return 0;
}

int Fn(int x)
{
	if (x == 0)//限制条件
		return 1;
	else
		return x * Fn(x - 1);

}

如上图举例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)=1开始->F(1)=1向上级返回F(2)=2*1=2->F(3)=6...直到F(4)被计算出来并返回给主函数。就是这一过程连续。

以上写法仅限x较小的情况,否则会出现整数溢出的情况。因为阶乘即使x在10范围已经很大了。

C语言递归解决爬楼梯问题

简单介绍一下,这是用递归解决著名青蛙跳台阶问题(换了一个形式)。

找规律解决

  遇到复杂问题进行拆分转换

试着分析前面几项,设台阶数为n

一  n=1

跳法只有一种,即1.

——————

二  n=2

有1+1,2两种情况,每种也只有一种小的跳法,合计两种。

——————

三   n=3

有1+1+1,1+2,3三种情况

1+1+1;

1+2,2+1(表示先后顺序,即先跳一步再跳两步,和先跳两步再跳一步是不同的);

3;

合计4种。

——————

   n=4

有1+1+1+1,1+1+2,1+3,2+2。(注意最多只能跳三步

1+1+1+1;

1+1+2,1+2+1,2+1+1;

1+3,3+1;

2+2;

合计7种。

——————

规律可能还不明显,试着计算n=5的情况,发现一共有13种。

我们试着写数列

1,2,4,7,13...

有从第四项开始,每一项等于其前三项之和,有Fn=F(n-1)+F(n-2)+F(n-3);这与斐波那契数列有共同之处。

所以我们就不难写出了。如图(记得引头文件)。

​​​​​​​​​​​​​​​​​​#include<stdio.h>
int fact(int x);//函数声明
int main()
{
	int n = 0;
	scanf("%d", &n);
	int a = fact(n);//接受一个整型(台阶数),返回一个整型(跳法)
	printf("%d", a);
	return 0;
}

int fact(int x)//函数定义
{
	if (x == 1)//一阶有一种跳法
		return 1;
	else if (x == 2)//二阶有两种跳法
		return 2;
	else if (x == 3)//三阶有四种跳法
		return 4;
	else
		return fact(x - 1) + fact(x - 2) + fact(x - 3);
}

 动态规划(了解一下)

我们先定义一个dp数组,dp[i]就是dp的数组的一个元素。它的含义是在从初始位置跳到第i阶的方法数。记住这个含义。

接下来找状态转移方程(递推公式)。

1阶,方法数是1;

2阶,方法数是2;

3阶,方法数是4;

由于一次最多跨三步;

那么四阶的方法数呢?很明显在一阶基础上在跨3步就到了,同样在二阶基础上跨两步就到了,三阶跨一步就行了。

所以四阶的方法数就是前面三者之和,1+2+4=7;

再推广到一般,dp[i]=dp[i-1]+dp[i-2]+dp[i-3],这就是这道题的状态转移方程(递推公式)

int main()
{
	int dp[47] = { 0 };
	dp[1] = 1;
	dp[2] = 2;
	dp[3] = 4;
	int n = 0;
	scanf("%d", &n);
	if (n > 0 && n <= 3)
		;
	else {
		for (int i = 4; i <= n; i++) {
			dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
		}
	}
	printf("%d", dp[n]);
	return 0;
}

 函数递归实现打印整数二进制

#include<stdio.h>
void to_binary(unsigned int n);
int main()
{
	unsigned int number = 0;
	printf("Please a integer to\n");
	while (scanf("%d", &number) == 1)
	{
		to_binary(number);
		puts("\n");
		printf("Please a integer to\n");
	}
}
//跟打印顺序十进制数1234一样
//比如某个二进制数10000 1000 100 10 1
//获取每个位数,然后从高位打印即可。
void to_binary(unsigned int n)
{
	int r = 0;
	r = n % 2;
	if (n >= 2)
		to_binary(n / 2);
	putchar(0 == r ? '0' : '1');

 

汉诺塔问题

给定三根柱子,记为 𝐴,𝐵,𝐶 ,其中 𝐴 柱子上有 𝑛 个盘子,从上到下编号为 0 到 𝑛−1 ,且上面的盘子一定比下面的盘子小。问:将 𝐴 柱上的盘子经由 𝐵 柱移动到 𝐶 柱最少需要多少次?

​ 移动时应注意(规则):

① 一次只能移动一个盘子

​ ②大的盘子不能压在小盘子上

如果A中只有一个盘子,那么显然直接挪到C盘就一次。

如果A中有两个盘子,那么按照规则 :

一共有三次。

那么状态一有一次,状态二有两次。状态三可以理解为有状态二确定。

 

 先把红色方框部分弄到B盘上,记为方法dp[2],那么dp[3]就可以理解为在dp[2]的基础上挪两次红色方框的整体和一次底盘。即dp[3]=2*dp[2]+1;

看到我用dp了,首先想到了动态规划。

动态规划

确定dp数组,这里用一维dp数组。dp[i]表示i个盘子从A盘挪到C盘的方法次数。

递推公式由前面易得dp[i]=2*dp[i-1]+1;

解释:

将n个盘子当作1个盘子和n-1个盘子整体化的两个盘子的情况。

dp[i]表示i个盘子从A盘挪到C盘的方法次数。

dp[i-1]表示i-1个盘子从A盘挪到C盘的方法次数。

而两个盘子的情况,需要挪动两次上面整体的盘子和一次最底部的盘子。

dp[i]=2*dp[i-1]+1;状态转移方程(递推公式)

接下来初始化dp数组,只需要初始化dp[0]=0即可。

int main()
{
	int dp[30];//按需求增大定长数组空间
	dp[0] = 0;//dp数组初始化
	int n = 0;//开局有几个盘子
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)//循环dp数组
	{
		dp[i] = 2 * dp[i - 1] + 1;
	}
	printf("%d", dp[n]);
	return 0;
}

递归 

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

int main()
{
	int n = 0;//开局有几个盘子
	scanf("%d", &n);
	
	printf("%d", f(n));
	return 0;
}

 当然,如果你一眼发现这个递推公式的通项公式是 dp[n]=2^n - 1;那么可以直接写成这样

#include<math.h>
int main()
{
	int n = 0;//开局有几个盘子
	scanf("%d", &n);
	
	printf("%d", (int)pow(2,n) - 1);
	return 0;
}

结尾

 2024.5.12早上更新了汉诺塔问题和动态规划的解法。

补充(爬楼梯问题循环优化)

试着运行代码,并对比两种代码输入较大数字的输出效率(注意别溢出哦),试着统计一下fact(4)被计算的次数吧,递归就一定好吗?最后,辛苦看到最后的各位,谢谢阅读。

  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值