小鑫的算法之路:leetcode0070 爬楼梯

题目

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

提示:

1 <= n <= 45

解法1:记忆化搜索

假如需要到达第n阶楼梯时,可以在n-1阶时爬1阶,或者n-2阶爬2阶到达。那么递推公式很容易得到,即f(n) = f(n - 1) + f(n - 2);

递归终止条件有两个,一个是n = 1时,f(1) = 1,因为只有一种方式到第一个台阶,另外一个是n = 2时,f(2) = 2,因为有两种方式到第二个台阶。

但是,很容易发现在该递归处理中有很多重复子问题,时间复杂度会达到O(2^n),时间复杂度极高。为了避免相同子问题的重复计算,需要对子问题的答案进行保存。在每次求解时,先判断是否存在该子问题答案,如果存在则直接使用,否则对子问题进行求解处理。

代码如下:

class Solution {
public:
    int climbStairs(int n) {
        if (result_[n] == 0) {
            result_[n] = climbStairs(n - 1) + climbStairs(n - 2);
        }

        return result_[n];
    }

private:
    // 由于当前题目的台阶范围为[1, 45], 因此初始化长度为46的数组, 除开索引为1和2的初始值分别为1和2外,其余值均为0
    array result_ = {{0, 1, 2}};
};

时间复杂度为O(n),空间复杂度为O(1),因为以46固定长度分配数组空间。执行结果如下:

小鑫的算法之路:leetcode0070 爬楼梯_第1张图片

解法2:动态规划

记忆化搜素属于自上而下的问题解决方式,本题也可使用自下而上的动态规划方式先对各个子问题求解, 最终得到问题的答案。

代码如下:

class Solution {
public:
    int climbStairs(int n) {
        // 由于当前题目的台阶范围为[1, 45], 因此初始化长度为46的数组, 除开索引为1和2的初始值分别为1和2外,其余值均为0
        array result = {{0, 1, 2}};  

        for (int i = 3; i <= n; ++i) {
            result[i] = result[i - 1] + result[i - 2];
        }

        return result[n];
    }
};

时间复杂度为O(n),空间复杂度为O(1),固定长度分配。执行结果如下:

小鑫的算法之路:leetcode0070 爬楼梯_第2张图片

解法3:内存优化后的动态规划

在解法2中,固定分配了长度为46的数组,但是针对本题,无需保存那么多子问题的解,通过递推公式发现只需保存前两个子问题的解即可。代码如下:

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

        if (n == 2) {
            return 2;
        }

        int pre_result = 2;
        int pre_two_result = 1;
        int result = 0;
        for (int i = 3; i <= n; ++i) {
            result = pre_result + pre_two_result;
            pre_two_result = pre_result;  // 先更新前两次子问题的结果
            pre_result = result;  // 再更新前一次子问题的结果
        }

        return result;
    }
};

时间复杂度为O(n),空间复杂度为O(1)。执行结果如下:

小鑫的算法之路:leetcode0070 爬楼梯_第3张图片

你可能感兴趣的:(动态规划)