【Leetcode】爬楼梯

2019-10-11

问题:

爬n阶楼梯,每次只能走1阶或者2阶,计算有多少种走法。

 

暴力计算+记忆化递归。

从位置 i 出发,每次走1阶或者2阶台阶,记录从位置 i 出发到目标 n 所有的走法数量,memoA[i] 。记录的数据可以重复使用,避免冗余计算。

时间复杂度:O(n)。每次计算从 i 位置出发到终点 n 的走法,共计算 n 次,即树形递归的大小为n。

空间复杂度:O(n)。使用了长度为 n 的数组。

clock_t start1, end1;

class Solution {
public:
    int climbStairs(int n) {
        
        int memo[n+1] = {0};
        
        start1 = clock();
        int result = climb_stairs(0, n, memo);
        end1 = clock();
        
        cout << "cost time = " << (double)(end1 - start1) / CLOCKS_PER_SEC << endl;
        
        return result;
    }
    
    int climb_stairs(int i, int nums, int memoA[]) {
        
        if (i > nums)
            return 0;
        
        if (i == nums)
            return 1;
        
        if (memoA[i] > 0)
            return memoA[i];
        
        memoA[i] = climb_stairs(i+1, nums, memoA) + climb_stairs(i+2, nums, memoA);        
        
        return memoA[i];
    }
};

 

动态规划

动态规划的关键步骤在于构建递归函数。由于第n级阶梯可由第n-1级(跨一步)和第n-2级(跨两步)到达。记 f(n) 为到达第n级阶梯的方案数量,则有递归函数 f(n) = f(n-1)+f(n-2).

1)动态规划第一种实现方法:

时间复杂度:O(2^n)。其运算过程就是一个完全二叉树的展开,共有 2^(n-2)+1 个节点,即递归运算了 2^(n-2)+1 次。

空间复杂度:O(n)。递归深度达到n层。

class Solution {
public:
    int climbStairs(int n) {
        if (n == 1)
            return 1;
        if (n == 2)
            return 2;
        
        int sumMethod = climbStairs(n-1) + climbStairs(n-2);
        
        return sumMethod;
    }
};

 

2)动态规划第二种实现方法:

时间复杂度:O(n)

空间复杂度:O(n)

class Solution {
public:
    int climbStairs(int n) {
        if (n == 1)
            return 1;
        
        int memo[n+1] = {0};
        memo[1] = 1;
        memo[2] = 2;
        
        for(int i = 3; i <= n; ++i) {
            memo[i] = memo[i-1] + memo[i-2];
        }
        
        return memo[n];        
    }
};

 

斐波那契数

将动态规划的第二种实现方法进行修改。第一项为1,第二项为2,斐波那契数的计算公式如下:

fib(n) = fib(n-1)+fib(n-2)。

时间复杂度:O(n)

空间复杂度:O(1)

 

还存在时间复杂度为 O(log(n))算法,其核心思想为直接找到第n个斐波那契数,而非进行遍历计算。

这就是算法的魅力。从暴力求解开始,逐步优化算法流程,砍掉冗余的计算步骤,尽可能去除遍历步骤。最终达到提升时间复杂度和空间复杂度的目的。