70. 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:

Input: 2
Output: 2
Explanation: There are two ways to climb to the top.

  1. 1 step + 1 step
  2. 2 steps

我的想法

brute force
按照题意把所有可能的组合遍历一次。结果对的,但是O(2^n)超时

class Solution {
    public int climbStairs(int n) {
        return helper(n, 0, 0);
    }
    private int helper(int n, int sum, int count) {
        if(sum + 1 <= n) count = helper(n, sum + 1, count);
        if(sum + 2 <= n) count = helper(n, sum + 2, count);
        if(sum == n) return ++count;
        return count;
    }
}

DP
把1~n之间每个数的解都求出来并存在dp数组中,
dp[i]
= dp[i-1] + dp[1]
= dp[i-2] + dp[2]

但是这样有重复项不知如何滤去

解答

leetcode solution 1: Brute force
同样也是超时,不过写法更漂亮

public class Solution {
    public int climbStairs(int n) {
        return climb_Stairs(0, n);
    }
    public int climb_Stairs(int i, int n) {
        if (i > n) {
            return 0;
        }
        if (i == n) {
            return 1;
        }
        return climb_Stairs(i + 1, n) + climb_Stairs(i + 2, n);
    }
}

leetcode solution 2: Recursion with Memoization
在这里插入图片描述
由上图可以看出,一个节点,如果值一定,其子孙节点一定。比如,当前节点为3,其子节点一定为4和5,再下一层一定为5,6,6,7。因此不管当前节点前面是如何连接的,只要节点的数值相同,则子孙节点情况一定相同。

因此,一旦某个节点满足条件的个数确定,其值可以复用。比如求值为5的情况,求得节点为4有1种情况,则不论其父节点是3还是2,都是1种情况。节点3有2种情况,则不论其是由1,1,11,2还是2,1组成,其结果都是2。

在这里是从后往前的计算方法

public class Solution {
    public int climbStairs(int n) {
        int memo[] = new int[n + 1];
        return climbHelper(n, memo, 0);
    }
    private int climbHelper(int n, int[] memo, int i) {
        if(i == n) return 1;
        if(i > n) return 0;
        //如果当前值i已经计算过,直接用
        if(memo[i] > 0) return memo[i];
        
        //如果当前值i没有计算过,则先计算其子树的值
        memo[i] = climbHelper(n, memo, i+1) + climbHelper(n, memo, i+2);
        return memo[i];
    }
}

leetcode solution 3: Dynamic Programming
这里像是将上一个方法的思想反过来,当前节点一定是由i-1和i-2,+1或者+2得来的,而与子孙节点无关。
自己差一点就做出来了,但是考虑的情况太多了,其实只需要考虑i-1和i-2。因为i-3、i-4,已经包括在了i-1和i-2的计算当中!!!!
最关键的点就是弄清楚什么是不变的量,在这里即为每个节点都只能由i-1和i-2,+1或者+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];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值