【题目描述】
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
【测试用例】
示例1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1.1阶 + 1阶
2.2阶
示例2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶
1.1阶 + 1阶 + 1阶
2.1阶 + 2阶
3.2阶 + 1阶
【思路分析】
这道题也算是一道很经典的题,读大学的时候学过,但不记得是在什么课上学过。用了两种方法求解。
先说一下这里的规律,一共n阶楼梯,每次只能走1阶或者2阶。
当n=1时,即爬1阶楼梯时,无疑只有一种方法,走1阶即可;
当n=2时,有两种方法,可以一次走2阶,也可以分两次每次走1阶;
当n=3时,爬3阶楼梯,可以从第1阶走2阶到第3阶,也可以从第2阶走1阶到第3阶,以此类推。换句话说就是,n>=3时,爬到n阶所用的方法总数可以由爬到n-1阶和爬到n-2阶推算出来,如果用func(n)表示爬n阶台阶的方法数,则func(n) = func(n-1) + func(n-2)。
法一:递归
递归方法,在代码中表现为函数的自身嵌套,这也是很好想到的一种方法。刚才讲了这道题的本质规律func(n) = func(n-1) + func(n-2),在代码中func就是求方法数的函数climbStairs(n)
当n<=2时,单独处理,直接返回n;
当n>2时,返回climbStairs(n-1) + climbStairs(n-2)。
ps:递归方法在力扣中提交时,C实现可以正常AC,但是C++实现会提示超时,可能力扣对这道题设置了时间?或者我代码没写对?想了半天也没找到原因。
法二:动态规划
动态规划是一种常见的算法思想,它是一种解决多阶段决策问题的优化方法。核心思想是将原问题分解为若干个相同的子问题,分别求解这些子问题并将其结果保存下来,可以有效避免重复计算,极大提升算法效率。
在本题中,爬n阶楼梯是原问题,爬n-1阶、n-2阶....1阶楼梯都是相同的子问题,他们的解决办法相同,只需要分别解决这些问题并将结果保存在一个数组fun中,那么 fun[i] 就是爬 i 阶楼梯的方法数,最后返回fun[n]即可。
这个数组也很好求,首先fun[1]=1,fun[2]=2(fun[0]不用管,可以为0也可以为1也可以任意值,为0表示站在0阶楼梯自然也不需要什么方法爬到0阶楼梯,为1可以理解为fun[2] = fun[1] + fun[0]的计算,这时遍历就是从2开始的),从fun[3]开始遍历,每个位置都等于其前两个位置的和。
空间复杂度优化
上面动态规划的空间复杂度是O(n),实际上可以优化为O(1)。O(n)是因为创建了一个长度为n的数组,但是实际上我们只需要两个变量one和two分别存储n=1和n=2时的方法数,然后在接下来的循环中,不断更新one和two的值:
①临时变量tmp=one+two (这里的tmp相当于刚才的fun数组中的每个位置的值,只是这里不用存储,只起临时中转的作用),②one=two,③two=tmp
使one和two始终存储着对应当前n值的func[n-2]和func[n-1](func表示爬n阶楼梯的方法数,这里只是方便说明而用这个符号)
举个例子,假如n=3,那么fun[3] = fun[2] + fun[1],而one对应的就是fun[1],two对应的就是fun[2],tmp=one+two也就是在计算fun[3]。而在n=4时,需要fun[3]和fun[2],代码中one=two就相当于让one指向了fun[2],代码中two=tmp就相当于让two指向了fun[3],就这样,在一轮轮的循环中,不用额外开辟空间存储每个n的结果,循环结束后two中保存的就是fun[n]的值,就达到了优化空间复杂度的目的。
【参考代码】
法一:C实现
#include <stdio.h>
//easy-70-爬楼梯
int climbStairs(int n);
int main(){
int n;
scanf("%d", &n);
int res = climbStairs(n);
printf("%d\n", res);
return 0;
}
//法一:递归
int climbStairs(int n) {
if(n<=2){
return n;
}else{
return climbStairs(n-1) + climbStairs(n-2);
}
}
法二:C实现
#include <stdio.h>
//easy-70-爬楼梯
int climbStairs(int n);
int main(){
int n;
scanf("%d", &n);
int res = climbStairs(n);
printf("%d\n", res);
return 0;
}
//法二:动态规划
int climbStairs(int n) {
if(n <= 2){
return n;
}
int fun[n+1];
fun[1] = 1;
fun[2] = 2;
int i;
for(i=3;i<n+1;i++){
fun[i] = fun[i-1] + fun[i-2];
}
return fun[n];
}
法二优化:C实现
#include <stdio.h>
//easy-70-爬楼梯
int climbStairs(int n);
int main(){
int n;
scanf("%d", &n);
int res = climbStairs(n);
printf("%d\n", res);
return 0;
}
//法二:动态规划优化
int climbStairs(int n) {
if(n <= 2){
return n;
}
int one = 1;
int two = 2;
int i;
for(i=3;i<n+1;i++){
int tmp = one + two;
one = two;
two = tmp;
}
return two;
}
法二:C++实现
#include <iostream>
#include <vector>
using namespace std;
//easy-70-爬楼梯
class Solution {
public:
int climbStairs(int n);
};
//法二:动态规划
int Solution::climbStairs(int n){
if(n <= 2){
return n;
}
vector<int> fun(n+1);
fun[1] = 1;
fun[2] = 2;
int i;
for(i=3;i<n+1;i++){
fun[i] = fun[i-1] + fun[i-2];
}
return fun[n];
}
int main(){
int n;
cin>>n;
Solution sol;
int res = sol.climbStairs(n);
cout<<res<<endl;
return 0;
}
法二优化:C++实现
#include <iostream>
using namespace std;
//easy-70-爬楼梯
class Solution {
public:
int climbStairs(int n);
};
//动态规划优化
int Solution::climbStairs(int n){
if(n <= 2){
return n;
}
int one = 1;
int two = 2;
int i;
int tmp;
for(i=3;i<n+1;i++){
tmp = one + two;
one = two;
two = tmp;
}
return two;
}
int main(){
int n;
cin>>n;
Solution sol;
int res = sol.climbStairs(n);
cout<<res<<endl;
return 0;
}