n级台阶 1,2,3 步走法

本文探讨了上台阶问题的两种解题方法,一种通过数学模型和排列组合来计算不同走法的数量,另一种则采用数学归纳法简化计算过程,提供了复杂度更低的解决方案。同时,对比了两种方法在代码实现上的效率差异。

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

 

最近看到一批面试题目,比较感兴趣,工作之余解解题来练习练习思维.

人人笔试1:一个人上台阶可以一次上1个,2个,或者3个,问这个人上n层的台阶,总共有几种走法?

思路:先建立数学模型,设3步的走 i 次,2步的走 j 次, 1步的走 k 次,上了3*i + 2*j + 1*k = n个台阶.总共走 i + j + k 次, 等于把n个台阶的长度先划分成 i + j + k 个段落, 然后分别填下i个3, j 个2, k个1.这样,当划分成 i + j + k 个段落时, 根据排列组合知识,所有填充方法有 (i + j + k )!/ ( i!*j!*k!) 种,程序中使用GetComb(i,j,k)函数计算此值.
对于i, j, k的确定,我们可以用从大到小划分法, 先划分3的次数,再划分2的次数,剩下的都算做1的次数,具体程序中就是里面的i,j,两重循环.


 

//辅助函数,计算阶乘
int Factorial(int n)
{
 int ret = n;
 if (n<=1)
 {
  return 1;
 }
 while (n-->1)
 {
  ret*=n;
 }
 return ret;
}


//求(i+j+k)!/(i!*j!*k!)

int GetComb(int i,int j,int k)
{
 int result = 1;
 int m = Factorial(i+j+k);
 int l =  Factorial(i)*Factorial(j)*Factorial(k);
 return m/l;
}

//主函数

int NStepFor123(int n)
{
    int i=0;
    int j=0;
    int p;
    int k;
    int result=0;
    for ( i=0; i<=n/3; i++ )
    {
        p = n-i*3;
        for ( j=0; j<=p/2; j++ )
        {
            k = p -j*2;
            //求(i+j+k)!/(i!*j!*k!)
            result += GetComb(i,j,k);
        }
    }
    return result;
}

 

另外有一种数学归纳法的思路先得出递推式:

f(n) = f(n-1)+f(n-2)+f(n-3),特别地f(0)=1;f(1)=1;f(2)=2;

式子的证明为:增加一步共为f(n+1)的时候,把这新的一步算进去后有三种情况,1是这一步仅当一步走为f(n)次,2是这一步配合原来的最后一步作为两步走为f(n-1)次,3是这一步配合前面的两步作三步走为f(n-2);所以式子f(n+1) =f(n)+ f(n-1)+f(n-2),归纳得证。

这种方法可以使复杂度降为O(n),而且只有加法,比上面的第一种方法要好。计算f(n)只要一直记录之前三个值,以求下一个值就可以了。以下是程序实现:

int f (int k)
{
	int v[3]={1,1,2};
	int index = -1;
	int i = 0;

	if (k<0)
	{
		return 0;
	}

	if (k<3)
	{
		return v[k];
	}

	while(k-->2)
	{
		index++;
		index %= 3;
		v[index] = v[0]+v[1]+v[2];		
	}
	return v[index];
}


 

### C语言实现n台阶每次1或2的解决方案 以下是基于递归和动态规划两种方式来解决该问题的具体实现。 #### 方一:递归解 递归的核心思想是将大问题分解成更小的子问题。对于本题来说,如果当前站在第`n`台阶,则可以通过从第`n-1`迈一到达,也可以从第`n-2`迈两到达。因此,总的方数等于前两台阶数之和[^1]。 ```c #include <stdio.h> int climbStairs(int n) { if (n <= 2) return n; else return climbStairs(n - 1) + climbStairs(n - 2); } int main() { int n = 0; printf("请输入台阶总数:"); scanf("%d", &n); int result = climbStairs(n); printf("总共 %d 种。\n", result); return 0; } ``` 此代码的时间复杂度较高,因为存在大量的重复计算。例如,在计算`climbStairs(5)`时会多次调用`climbStairs(3)`和`climbStairs(4)`[^2]。 --- #### 方二:动态规划解 为了避免递归带来的冗余计算,可以采用动态规划的思想。通过构建一个数组记录每层台阶对应的数量,从而减少不必要的重复运算[^3]。 ```c #include <stdio.h> int climbStairsDP(int n) { if (n <= 2) return n; int dp[n + 1]; dp[1] = 1; dp[2] = 2; for (int i = 3; i <= n; ++i) { dp[i] = dp[i - 1] + dp[i - 2]; // 状态转移方程 } return dp[n]; } int main() { int n = 0; printf("请输入台阶总数:"); scanf("%d", &n); int result = climbStairsDP(n); printf("总共 %d 种。\n", result); return 0; } ``` 这种方的空间复杂度为O(n),时间复杂度降为线性别O(n)。 --- #### 方三:优化版动态规划(空间优化) 进一观察发现,实际上只需要保存最近两次的结果即可完成状态更新,无需维护整个数组。这使得空间复杂度降低至常量别O(1)[^4]。 ```c #include <stdio.h> int climbStairsOptimized(int n) { if (n <= 2) return n; int prev1 = 1, prev2 = 2, current = 0; for (int i = 3; i <= n; ++i) { current = prev1 + prev2; // 更新当前值 prev1 = prev2; // 将prev2赋给prev1 prev2 = current; // 更新prev2 } return prev2; } int main() { int n = 0; printf("请输入台阶总数:"); scanf("%d", &n); int result = climbStairsOptimized(n); printf("总共 %d 种。\n", result); return 0; } ``` 上述代码不仅提高了效率,而且减少了内存占用。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值