青蛙跳台阶—递归思想的实现

本文探讨了青蛙跳台阶问题及其两种变种,分析了递归解法的效率问题并提出了优化方案。对于原问题,通过斐波那契数列解决,递归实现会导致重复计算,改用循环则能显著提高效率。变种1中,青蛙可以跳1到n级台阶,得出方法数为2的(n-1)次方。变种2考虑了青蛙可以跳1到m级台阶的情况,通过递归算法解决了n大于m和小于等于m两种情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原问题

  一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

  思路:既然青蛙一次可以跳上1个1级台阶或者一次跳上1个2级台阶,那么跳上一个n级台阶的方法:

  • 要么是先跳上一个n-1级台阶然后再跳一个1级台阶做到;

  • 要么是跳上一个n-2级台阶然后再跳上一个2级台阶实现;

所以跳上n级台阶的方法数等于跳上n-1级台阶的方法数加上跳上n-2级台阶的方法数,用f(n)表示跳上n级台阶的方法数,经过我们的分析,有
f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n1)+f(n2)

f ( 1 ) = 1 f(1)=1 f(1)=1

f ( 2 ) = 2 f(2)=2 f(2)=2

  这不就是个斐波那契数列嘛,我们不妨用递归思想来实现这个计算。

#include <stdio.h>
long long Fib(int n)
{
	if (n == 2)
	{
		return 2;
	}
    else if(n==1)
    {
        return 1;
    }
	else
	{
		return Fib(n - 1) + Fib(n - 2);
	}
}
int main()
{
	int n;
	printf("请输入n的值\n");
	scanf("%d", &n);
	long long a = Fib(n);
	printf("%lld\n", a);
	return 0;
}

  但是实际运行发现,当n取到40左右的数字的时候,这个程序就需要运行好久的时间,这是为什么呢?原因是我们在做这个递归的时候进行了大量的重复计算。

在这里插入图片描述

  可以看到,仅仅是进行到第3层,我们已经重复计算了4次f(46),3次f(47),3次f(45),我们接着往下要运行完整个f(50)要进行不知道多少次的重复计算,很多重复计算都完全没有必要,既然我已经算出来了,为什么不直接调用还要再重复计算,浪费时间,因此,我们可以不用递归的思想来实现这个问题:

long long Fib(int n)
{
	int a = 1;
    int b = 2;
    int c = 3;
    if(n==1)
    {
        return a;
    }
    else if(n==2)
    {
        return b;
    }
    else
    {
        while(n>2)
        {
            c=a+b;
            a=b;
            b=c;
            n--;
        }
        return c;
    }
}


int main()
{
	int n;
	printf("请输入n的值\n");
	scanf("%d", &n);
	long long a = Fib(n);
	printf("%lld\n", a);
	return 0;
}

可以发现这次的运行速度明显快了很多。

变种1

  一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

思路1:和上面问题的分析思路类似,既然要跳n级台阶,我们一共有n种完成方法:

  • 一次跳n-1级,然后跳1级
  • 一次跳n-2级,然后跳2级
  • 一次跳1级,然后跳n-1级
  • 直接跳n级

  所以跳n级台阶的方法数等于跳n-1级台阶的方法数加跳n-2级台阶的方法数加…加跳一级台阶的方法数+直接跳n级(方法数是1)

  同样以f(n)代表跳n级台阶的方法数,根据我们上面的分析,有:
f ( n ) = f ( n − 1 ) + f ( n − 2 ) + . . . + f ( 1 ) + 1 f(n)=f(n-1)+f(n-2)+...+f(1)+1 f(n)=f(n1)+f(n2)+...+f(1)+1
求解这个数列的通项公式,我们不妨在写一下f(n-1)的表达式
f ( n − 1 ) = f ( n − 2 ) + . . . + f ( 1 ) + 1 f(n-1)=f(n-2)+...+f(1)+1 f(n1)=f(n2)+...+f(1)+1
两式作差
f ( n ) − f ( n − 1 ) = f ( n − 1 ) f(n)-f(n-1)=f(n-1) f(n)f(n1)=f(n1)

f ( n ) = 2 f ( n − 1 ) f(n)=2f(n-1) f(n)=2f(n1)

f ( n ) 是 一 个 等 差 数 列 , 公 差 为 2 , 首 项 f ( 1 ) = 1 f(n)是一个等差数列,公差为2,首项f(1)=1 f(n)2f(1)=1

f ( n ) = 2 n − 1 f(n)=2^{n-1} f(n)=2n1

思路2. 因为我们可以一次直接跳1~n级台阶,所以不管是什么方法,要到达n级台阶,n级台阶必须要站上去,其他的台阶都可以选择站上去或者不站上去,因此总共有2^(n-1)种可能。

这个问题我们可以直接用pow函数求解,当然也可以用递归实现

//递归

unsigned long long frog(int n)
{
	if (n > 1)
	{
		return 2 * frog(n - 1);
	}
	else
	{
		return 1;
	}
}

int main()
{
	int n;
	printf("请输入n的值\n");
	scanf("%d", &n);
	unsigned long long a = frog(n);
	printf("%llu\n", a);
	return 0;
}

//pow函数

#include <stdio.h>
#include <math.h>

int main()
{
	int n;
	printf("请输入n的值\n");
	scanf("%d", &n);
	unsigned long long b = (unsigned long long)pow(2, n - 1);
	printf("%llu\n", b);
	return 0;
}

变种2

  一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上m级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

  这个问题显然是要分类讨论的,因为我们不知道n和m的大小关系。

① n<=m

  这个情况显然是简单的,因为青蛙可以一次跳上m级由于m>=n青蛙也一定可以一次跳上n级,我们可以把这种情况转化为变种1的问题
f ( n ) = 2 f ( n − 1 ) f(n)=2f(n-1) f(n)=2f(n1)

f ( n ) = 2 n − 1 f(n)=2^{n-1} f(n)=2n1

② n>m

  这个情况下,我们不能一次跳到n级,要跳上n级,

  • 要么跳上n-1级然后再跳上1级;
  • 要么跳上n-2级然后再跳上2级;
  • 要么跳上n-m级然后再跳上m级;

注意:往下就没有情况了,因为我们每次罗列的跳上n级的方法都是后面的动作是1步可以完成的,我们的青蛙并不能一次跳m+1级

  所以一次跳n级的方法数等于一次跳n-1级的方法数加一次跳n-2级的方法数加一次跳n-3级的方法数加…加一次跳n-m级的方法数。

  以f(n)作为跳n级的方法数,经过我们的分析,有
f ( n ) = f ( n − 1 ) + . . . + f ( n − m ) f(n)=f(n-1)+...+f(n-m) f(n)=f(n1)+...+f(nm)
  我们要能用递归来处理的话,递归式子算数因子个数不能在自动变化,因此我们再化简一下上式
f ( n − 1 ) = f ( n − 2 ) + . . . f ( n − 1 − m ) f(n-1)=f(n-2)+...f(n-1-m) f(n1)=f(n2)+...f(n1m)
两式作差
f ( n ) − f ( n − 1 ) = f ( n − 1 ) − f ( n − 1 − m ) f(n)-f(n-1)=f(n-1)-f(n-1-m) f(n)f(n1)=f(n1)f(n1m)

f ( n ) = 2 f ( n − 1 ) − f ( n − 1 − m ) f(n)=2f(n-1)-f(n-1-m) f(n)=2f(n1)f(n1m)

我们用c语言实现如下:

#include <stdio.h>

int frog(int n, int m)
{
	if (n > m)
	{
		return 2 * frog(n - 1, m) - frog(n - 1 - m, m);
	}
	else
	{
		if (n <= 1)
		{
			return 1;
		}
		return 2 * frog(n - 1,m);
	}
}

int main()
{
	int m, n;
	printf("请输入m和n 以m n的形式输入\n");
	scanf("%d %d", &m, &n);
	int b=frog(n,m);
	printf("%d", b);
	return 0;
}

1, m) - frog(n - 1 - m, m);
}
else
{
if (n <= 1)
{
return 1;
}
return 2 * frog(n - 1,m);
}
}

int main()
{
int m, n;
printf(“请输入m和n 以m n的形式输入\n”);
scanf("%d %d", &m, &n);
int b=frog(n,m);
printf("%d", b);
return 0;
}


<think>根据引用内容,青蛙台阶问题是一个经典的递归问题,其规律与斐波那契数列类似。问题描述如下: - 一只青蛙一次可以上1级台阶,也可以上2级台阶(有些问题中还可以3级,但这里以最常见的两种法为例)。 - 求该青蛙上一个n级的台阶总共有多少种法。 通过引用[3]和引用[5]的分析,我们可以得到: - 当n=1时,只有1种法(1级)。 - 当n=2时,有2种法(一次2级,或分两次每次1级)。 - 当n>2时,青蛙的最后一步有两种选择:1级或2级。如果最后一步1级,则前面的法为f(n-1);如果最后一步2级,则前面的法为f(n-2)。因此,总法数为f(n) = f(n-1) + f(n-2)。 这实际上就是斐波那契数列的递推关系,只是初始值不同: - 斐波那契数列:F(0)=0, F(1)=1, F(2)=1, F(3)=2, ... - 青蛙台阶1或2级):f(1)=1, f(2)=2, f(3)=3, f(4)=5, ... 即从f(1)开始就是斐波那契数列的移位。 因此,我们可以用递归方法实现,但需要注意递归的终止条件。 C++递归实现代码: ```cpp #include <iostream> int jumpWays(int n) { if (n <= 0) return 0; // 没有台阶法为0 if (n == 1) return 1; // 1级台阶只有1种法 if (n == 2) return 2; // 2级台阶有2种法 return jumpWays(n-1) + jumpWays(n-2); // 对于n>2 } int main() { int n; std::cout << "请输入台阶数: "; std::cin >> n; std::cout << "法总数: " << jumpWays(n) << std::endl; return 0; } ``` ### 代码说明: 1. **递归终止条件**: - $n \leq 0$:返回0,因为台阶数非正时没有法。 - $n=1$:返回1(只有一种法:1级)。 - $n=2$:返回2(两种法:连续两次1级,或一次2级)。 2. **递归关系**: - 当 $n>2$ 时,法数等于上 $n-1$ 级台阶法数加上上 $n-2$ 级台阶法数,即: $$f(n) = f(n-1) + f(n-2)$$ 3. **示例**: - 输入 $n=3$,输出 $3$(法:1+1+1, 1+2, 2+1)。 - 输入 $n=4$,输出 $5$(法:1+1+1+1, 1+1+2, 1+2+1, 2+1+1, 2+2)。 ### 注意事项: 1. **时间复杂度**:$O(2^n)$,指数级,效率较低。当 $n$ 较大时(如 $n>40$)会非常慢。 2. **重复计算问题**:递归过程中会重复计算很多子问题(如计算 $f(5)$ 时需要计算 $f(4)$ 和 $f(3)$,而计算 $f(4)$ 时又会计算 $f(3)$ 和 $f(2)$)。 3. **栈溢出风险**:递归深度过大可能导致栈溢出。 ### 优化建议: 1. **记忆化递归**:使用数组或哈希表存储已计算的子问题结果,避免重复计算,将时间复杂度降为 $O(n)$。 2. **动态规划**:使用循环自底向上计算,避免递归开销(见引用[4])。 > 如果青蛙一次可以1级、2级或3级台阶(如引用[2]所述),则递推关系变为 $f(n) = f(n-1) + f(n-2) + f(n-3)$,终止条件需增加 $n=3$ 的情况($f(3)=4$)[^2]。 §§相关问题§§ 1. 如何用动态规划优化青蛙台阶问题? 2. 青蛙台阶问题的时间复杂度如何分析? 3. 青蛙台阶问题与斐波那契数列有什么联系和区别? 4. 如果青蛙一次可以任意阶(1~n级),法总数是多少? 5. 记忆化递归C++中如何实现? [^1]: 青蛙台阶问题的递归关系与斐波那契数列类似。 [^2]: 当青蛙一次可以1、2或3级时,递推关系变为三项和。</think>### C++递归实现青蛙台阶问题 青蛙台阶问题是一个经典的递归问题,其核心思想是通过分解问题为更小的子问题来解决。根据台阶数的不同,青蛙法遵循特定的规律: #### 问题分析 1. **台阶数与法关系**: - 当 $n=1$ 时:只有1种法(直接1级)[^3] - 当 $n=2$ 时:有2种法(1+1 或 直接2级)[^3] - 当 $n=3$ 时:有3种法(1+1+1, 1+2, 2+1)[^1] - 当 $n>3$ 时:法数 = $n-1$ 级的方法 + $n-2$ 级的方法[^5] 2. **递推公式**: $$f(n) = \begin{cases} 1 & n=1 \\ 2 & n=2 \\ f(n-1) + f(n-2) & n>2 \end{cases}$$ #### C++递归实现代码 ```cpp #include <iostream> int frogJump(int n) { // 基础情况处理 if (n <= 0) return 0; // 无效台阶数 if (n == 1) return 1; // 只有1种法 [^3] if (n == 2) return 2; // 有2种法 [^3] // 递归关系:f(n) = f(n-1) + f(n-2) [^5] return frogJump(n - 1) + frogJump(n - 2); } int main() { int steps; std::cout << "请输入台阶数: "; std::cin >> steps; std::cout << "法总数: " << frogJump(steps) << std::endl; return 0; } ``` #### 代码说明 1. **递归终止条件**: - $n=1$:返回1(直接1级) - $n=2$:返回2(1+1 或 直接2级) 2. **递归关系**: $$f(n) = \text{frogJump}(n-1) + \text{frogJump}(n-2)$$ 表示最后一步可以选择1级(剩余 $n-1$ 级)或2级(剩余 $n-2$ 级)[^5] 3. **示例输出**: ```bash 请输入台阶数: 3 法总数: 3 # (1+1+1, 1+2, 2+1) ``` #### 复杂度与优化 1. **时间复杂度**:$O(2^n)$,存在大量重复计算 2. **优化建议**: - 使用记忆化存储(缓存已计算结果) - 改用动态规划(时间复杂度 $O(n)$)[^4] - 对于能3级的情况,递推式改为 $f(n) = f(n-1) + f(n-2) + f(n-3)$[^2] > 此实现直观展示了递归思想,但实际应用中应考虑优化方案以避免指数级时间复杂度[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值