Leetcode70. Climbing Stairs
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?
Note: Given n will be a positive integer.
Example 1:
Input: 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps
Example 2:
Input: 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step
解法一:暴力法
要求 n 层的台阶的走法,由于一次走 1 或 2 个台阶,所以上到第 n 个台阶之前,一定是停留在第 n - 1 个台阶上,或者 n - 2 个台阶上。所以如果用 f ( n ) 代表 n 个台阶的走法。那么f(n) = f(n - 1) + f(n - 2)
,f(1) = 1
,f(2) = 2
。发现个神奇的事情,这就是斐波那契数列(Fibonacci sequence)。
public int climbStairs(int n) {
return climbStairsN(n);
}
private int climbStairsN(int n) {
if (n == 1) {
return 1;
}
if (n == 2) {
return 2;
}
return climbStairsN(n - 1) + climbStairsN(n - 2);
}
- 时间复杂度:
- 空间复杂度:
解法二:记忆化递归
在上一种方法中,我们计算每一步的结果时出现了冗余。另一种思路是,我们可以把每一步的结果存储在memo数组中,每当函数再次被调用,我们就直接从 memo数组中返回结果。这样我们得到一颗修复的递归树。
public int climbStairs(int n) {
int memo[] = new int[n + 1];
return climb_Stairs(0, n, memo);
}
public int climb_Stairs(int i, int n, int memo[]) {
if (i > n) {
return 0;
}
if (i == n) {
return 1;
}
if (memo[i] > 0) {
return memo[i];
}
memo[i] = climb_Stairs(i + 1, n, memo) + climb_Stairs(i + 2, n, memo);
return memo[i];
}
- 时间复杂度:
- 空间复杂度:
解法三:动态规划
令dp[i]
是可以达到第i
阶的方法总数,则dp[i] = dp[i-1]+dp[i-2]
。
public class Solution {
public int climbStairs(int n) {
if (n == 1) {
return 1;
}
int[] dp = new int[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];
}
}
- 时间复杂度:
- 空间复杂度:
思考状态压缩,因为每次只需要用到dp[i-1]与dp[i-1]两个数,因此可以用常数替换数组。
public class Solution {
public int climbStairs(int n) {
if (n == 1) {
return 1;
}
int first = 1;
int second = 2;
for (int i = 3; i <= n; i++) {
int third = first + second;
first = second;
second = third;
}
return second;
}
}
- 时间复杂度:
- 空间复杂度:
解法四:矩阵相乘
定义一个矩阵 ,要求f(n)的话,我们先让 Q 矩阵求幂,然后取第一行第一列的元素,也就是
public int climbStairs(int n) {
int[][] Q = {{1, 1}, {1, 0}};
int[][] res = pow(Q, n);
return res[0][0];
}
public int[][] pow(int[][] a, int n) {
int[][] ret = {{1, 0}, {0, 1}};
while (n > 0) {
//最后一位是 1,加到累乘结果里
if ((n & 1) == 1) {
ret = multiply(ret, a);
}
//n 右移一位
n >>= 1;
//更新 a
a = multiply(a, a);
}
return ret;
}
public int[][] multiply(int[][] a, int[][] b) {
int[][] c = new int[2][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
}
}
return c;
}
- 时间复杂度: (求幂)
- 空间复杂度:
更快的求幂:解法三
解法五:公式法
public int climbStairs(int n) {
double sqrt5=Math.sqrt(5);
double fibn=Math.pow((1+sqrt5)/2,n+1)-Math.pow((1-sqrt5)/2,n+1);
return (int)(fibn/sqrt5);
}
- 时间复杂度: (求幂)
- 空间复杂度: