Description:
you are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
问题描述:
爬楼梯问题,楼梯总共有n级台阶,你可以每次爬1步台阶或者每次跳2步台阶,那么请问爬到n级台阶共有多少种方法。
解法一:
思路:
这个问题给人的第一感觉是用递归解决,要爬到第n级台阶处,那么自然是有两种情况构成的,在n-1级台阶处,可以直接爬1步;或者在第n-2级台阶处,跳2步爬上。思路简单,但是耗时间,耗空间,接下来的解法用动态规划解决。
Code:
public int climbStairs(int n){
if(n == 1) return 1;
if(n == 2) return 2;
return climbStairs2(n-1) + climbStairs2(n-2);
}
解法二:
思路:
之所以说这道题可以用动态规划解决,其实这道题和斐波拉契数列很想,动态规划的核心是两点,状态和状态转移方程。动态规划的范围很广,不局限于解决递归问题。
这里为了把动态规划的思路说清楚,我穷举下。
0.爬1级楼梯,有0种方法
1.爬1级楼梯,有一种方法。
2. 爬2级台阶,有2种方法
3. 爬3级台阶,可以是爬1步+爬(3-1)级台阶和跳2步+爬(3-2)级台阶方法的总和为2+1=3
4. 爬4级台阶,可以是爬1步+爬(4-1)级台阶和跳2步+爬(4-2)级台阶方法的总和为2+3=5
5. 爬5级台阶,可以是爬1步+爬(5-1)级台阶和跳2步+爬(5-2)级台阶方法的总和为3+5=8
…….
看成数字序列就是 1 2 3 5 8 13 21 ….
明显是变初项的斐波拉契数列
我想这里我应该解释清楚了,既然斐波拉契数列可以用动态规划解决,那么这道题也可以。
这里的动态规划是避免了重复计算子问题,但是空间复杂度还可以降。
Code:
public int climbStairs3(int n){
int[] dp = new int[n+1];
dp[0] = 0;
dp[1] = 1;
dp[2] = 2;
for(int i = 3;i <= n; i++){
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
解法三:
思路:
在动态规划降空间复杂度的方法中,在这里有两种方法。这里先将滚动数组,所谓滚动数组这个名词说起来玄乎,其实就是不断对数组中存储的元素进行循环刷新,让数组的空间被滚动的利用。
这个题目里a[n] = a[n-1] + a[n-2] , 所以真正需要的就是3个变量。所以不要向解法二那样分配n+1维数组了,只需要int[3],然后利用 %(取模)运算符滚动刷新数组中的内容就可以了、
Code:
public int climbStairs4(int n){
int[] dp = new int[3];
dp[0] = 1;
dp[1] = 1;
for(int i = 2;i <= n; i++){
dp[i % 3] = dp[(i-1) % 3] + dp[(i-2) % 3];
}
return dp[n % 3];
}
解法四:
思路:
既然我知道只需要三个变量即可,那么回到问题本质,我就只用三个变量one_step_before two_step_before all_ways
one_step_before代表距离终点差一步时所有的方式
two_step_before代表距离终点差两步时所有的方式
all_ways代表去终点所有的方式
所以可以用依次赋值的方式,将one_step_before的值赋给two_step_before,那么two_step_before就唯一确定(本来的不确定态变为确定态), 将all_ways的值赋给one_step_before,那么得到更新的one_step_before。
all_ways = one_step_before + two_step_before;
two_step_before = one_step_before;
one_step_before = all_ways;
以上代码的次序很重要
Code:
public int climbStairs(int n) {
//base case
if(n <= 0) return 0;
if(n == 1) return 1;
if(n == 2) return 2;
int one_step_before = 2;
int two_step_before = 1;
int all_ways = 0;
for(int i = 2; i< n; i++){
all_ways = one_step_before + two_step_before;
two_step_before = one_step_before;
one_step_before = all_ways;
}
return all_ways;
}