LeetCode刷题笔记-70.爬楼梯(climbing-stairs)

问题描述

假设你正在爬楼梯。需要n阶你才能到达楼顶。
每次你可以爬12个台阶。你有多少种不同的方法可以爬到楼顶呢?

说明:给定n是一个正整数。



示例 1

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

示例 2

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶



题解

1.动态规划解法

算法解析

先从最基本情况分析,设f(n)表示上n阶台阶的走法个数:

  • n = 1时, f(1) = 1,第0阶\(\rightarrow\)第1阶跨1步为一种
  • n = 2时, f(2) = 1 + 1 = 2,第0阶\(\rightarrow\)第1阶\(\rightarrow\)第2阶跨1步为一种,第0阶\(\rightarrow\)第2阶跨2步为一种
  • n = 3时, f(3) = 1 + 2 = 3,第1阶\(\rightarrow\)第3阶跨2步为一种,第2阶\(\rightarrow\)第3阶跨1步为两种
  • n = 4时, f(4) = 3 + 2 ,第3阶\(\rightarrow\)第4阶跨1步有三种,第2阶\(\rightarrow\)第4阶跨2步有两种

通过以上分析可以发现:若想走到第n阶台阶,无外乎两种情况:

  • 从第n-1阶台阶跨一步走到第n阶台阶,此时走法为f(n-1).
  • 从第n-2阶台阶跨两步走到第n阶台阶,此时走法为f(n-2).

因此状态转移方程f(n)为:\(f(n) = f(n-1) + f(n-2)\)
至于状态转移方程为何不是\(f(n) = f(n-1)+1 + f(n-2)+2\)或者\(f(n) = f(n-1)+1 + f(n-2)+1\),是因为f(n)是爬楼梯方法数量,不是爬到n阶楼梯的步数,应该用乘法计算,即\(f(n) = f(n-1)*1 + f(n-2)*1\)

参考文章及资料

LeetCode官方题解 方法一:动态规划
LeetCode精选题解 第二种思路

复杂度分析
  • 时间复杂度: \(O(N)\), 总计循环n
  • 空间复杂度: \(O(1)\), 采用滚动数组时只需要固定的额外空间

代码实现
  • Java版(滚动数组)
class Solution {
    public int climbStairs(int n) {
        int[] scrollArr = {0, 0, 1}; // 滚动数组,节约空间
        for (int i = 0; i < n; ++i) {
            scrollArr[0] = scrollArr[1];
            scrollArr[1] = scrollArr[2];
            scrollArr[2] = scrollArr[0] + scrollArr[1];
        }
        return scrollArr[2];
    }
}
        
  • Python版
class Solution:
    def climbStairs(self, n: int) -> int:
        dp = [0 for i in range(n+2)] # 未采用滚动数组
        dp[1] = 1
        dp[2] = 2

        for i in range(3, n+1):
            dp[i] = dp[i-1] + dp[i-2]

        return dp[n]
        


2.Fibonacci解法

观察动态图规划解法中的f(n)数字规律可以发现,f(n)就是斐波那契(Fibonacci)数列.因此本题也可以直接求Fibonacci数列.

算法解析

参考文章及资料

LeetCode官方题解 方法三:通项公式
百度百科:斐波那契数列

复杂度分析
  • 时间复杂度: \(O(N)\),采用一般迭代法求Fibonacci数列的时间复杂度为\(O(N)\),若采用公式法时间复杂度一般为\(O(logN)\)
  • 空间复杂度: \(O(1)\),只需要固定的额外空间

代码实现
  • Java版(一般迭代法)
class Solution {
    public int climbStairs(int n) {
        int a = 0, b = 1, temp;
        for (int i = 0; i < n; ++i) {
            temp = b;
            b = a + b;
            a = temp;
        }
        return b;
    }
}
  • C版(一般迭代法)
int climbStairs(int n)
{
    int i;
    int a = 0, b = 1, temp;

    for (i = 0; i < n; ++i)
    {
        temp = b;
        b = a + b;
        a =temp;
    } 
    return b;
}
  • Python版(一般迭代法)
class Solution:
    def climbStairs(self, n: int) -> int:
        a, b = 0, 1;
        for i in range(n):
            a, b = b, a+b
        
        return b
  • Python版(通项公式法)
class Solution:
    def climbStairs(self, n: int) -> int:
        sqrt5 = math.sqrt(5)
        return round((pow((1+sqrt5)/2, n+1) - pow((1-sqrt5)/2, n+1)) / sqrt5)

注意:在使用公式法时,公式是从n=0开始计数,所以应该采用n+1计算nFibonacci数列值.




解题误区及心得总结

误区
  • 在推导状态方程时,没有正确理解状态方程含义导致采用加法计算状态转移方程
心得总结
  • 公式法求Fibonacci数列

  • 一般迭代法求解Fibonacci数列的代码还可以更加优化(如下所示)

  • C版(国际站讨论代码)

int climbStairs(int n) 
{
    int a = 1, b = 1;
    while (n--)
        a = (b += a) - a; // 很有技巧性
    return a;
}
  • Python版(国际站讨论代码)
a, b = 1, 1+n%2 # 减少了迭代次数
for i in range(int(n/2)):
    a += b # 等价于: a = a + b
    b += a # 等价于: b = (a + b) + b = a + 2b 
return a

参考文章及资料

LeetCode-Discuss 3-4 short lines in every language
LeetCode-Discuss @qeatzy ⭐643 April 16, 2016 1:19 PM

posted @ 2021-01-21 16:52  一生至为你  阅读(176)  评论(0)    收藏  举报