zoukankan      html  css  js  c++  java
  • LeetCode Notes_#70 Climbing Stairs

    LeetCode Notes_#70 Climbing Stairs

    Contents

    题目

    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

    思路

    1.暴力法

    public int climbStairs(int n) {
        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);
    }

    递归树
    递归树

    自己想了一会,感觉按照之前的那种循环遍历的方法行不通,原来是需要使用递归的方法。

    • 其中i是当前已经上的阶梯数,n是输入的需要上的总阶梯数。
    • 两个if语句是最后一步用的,最后一步如果刚好i==n+1超过了n,一定是climb_Stairs(i+2,n),返回0,这个不能计数;最后一步如果刚好i=n,一定是调用了climb_Stairs(i+1,n),计数需要加1。
    • 什么是递归呢?在这一个例子当中,为了求出climb_Stairs(0,5),需要先求出climb_Stairs(1,5)climb_Stairs(2,5),为了求出climb_Stairs(1,5),又要先求出climb_Stairs(2,5)climb_Stairs(3,5)...以此类推,就是上面的树的结构。只有计算到树的最末端,才能一步一步代回到上面,回溯到根节点。

    递归与迭代的比较

    • 迭代是不断地处理一个变量,每一次迭代就把这个变量更新一次(丢弃中间的结果),得到一个最终结果;
    • 递归是每一轮都无法得到结果,而是每一轮得到一个新问题,直到计算到最后,从最后一个问题开始回答,一步一步回溯回去,得到最开始那个问题的答案。

    提交结果:超时,所以这种解法是不行的,只能作为学习,继续看别的题解。

    2.记忆化递归

    在解法1里边的递归树的图解中,可以看到

    • 每个节点都是(i,5)也就是说climb_Stairs(i,n)的后一个参数其实都是固定的,就是input的n
    • 有一些节点是重复的,比如(2,5)(3,5)(4,5)都是出现了好几次,这里n比较小,所以重复不多,但是要是n是几十的话,那么就有很多的重复项,相当于是重复计算了,所以需要进行优化
      优化方法:
    • 用一个数组memo[i]记录climb_Stairs(i,n)的结果,在进行递归调用之前,先判断memo数组里边是否有我需要计算的数,有的话就直接返回之前存好的;
    • 如果是之前没有记录过的memo[i],就得记录一下
    class Solution {
        public int climbStairs(int n) {
            int memo[] = new int[n + 1];
            return climb_Stairs(0,n,memo);
        }
        private int climb_Stairs(int i,int n,int memo[]){//一个细节:这个地方函数类型其实可以写private,在类内可以相互调用即可
        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];
        }
    }

    提交结果:Beat 100%.

    3.动态规划(Dynamic Programming,DP)

    另一种思路,对于除了1,2之外的n的输入,都有以下公式成立:dp(i)=dp(i-1)+dp(i-2),因为:走到i-1之后只能选择再走1步达到i,走到i-2之后只能选择再走2步达到i。
    其实到这里,也就能看出这是斐波那契数列了,可以用斐波那契数列的解法解决。

    public class Solution{
        public int climbStairs(int n){
            if(n == 1) return 1;//ERROR:corner case处理
        int dp[] = new int[n+1];//如果是n,输入2就会出现越界,仅仅是为了防止越界,其实计算只需要计算到dp[n]就够了
        dp[1] = 1;
        dp[2] = 2;
        for(int i = 3;i <= n;i++){
        dp[i] = dp[i-1] + dp[i-2];
        }
        return dp[n];
        }
    }

    提交结果:beat 100%

    4.斐波那契数列

    由方法3观察dp数组的规律可以得到,其实爬到n阶台阶的方法数就是以n作为下标的斐波那契数列。
    那么问题转化为一个计算斐波那契数列的第n项。

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

    简单的想法是直接使用递归,这样写提交结果是时间超过限制。当输入参数n比较大的时候,按照递归的方法,需要回溯到n==1,n==2的情况,所以会速度很慢。
    解决方法是“以空间换时间”,维护3个变量,first,second,third,并且不断更新,这样就免去了重复的计算过程。

    class Solution {
        public int climbStairs(int n) {
            if(n == 1) return 1;//corner case
            int first = 1;
            int second = 2;
            for(int i = 3;i <= n;i++){//第一个third是dp[3]
                int third = first + second;
                first = second;
                second = third;//更新
            }
            return second;//最后climeStairs(n)就是更新之后的second
        }
    }

    分析:其实方法4和方法3几乎是一样的,区别是方法4只使用了3个变量,空间复杂度为O(1),而方法3需要使用n个变量,空间复杂度为O(n)。

    官方题解中还有5,6两种方法,个人觉得有点冷门,所以就不写了。

    参考

    爬楼梯题解-力扣(LeetCode)

  • 相关阅读:
    JS站点
    1011 World Cup Betting (20分)
    1007 Maximum Subsequence Sum (25分)(动态规划DP)
    1006 Sign In and Sign Out (25分)
    1005 Spell It Right (20分)
    1004 Counting Leaves (30分)(DFS)
    1003 Emergency (25分)(Dijkstra算法)
    1002 A+B for Polynomials (25分)
    1001 A+B Format (20分)
    canvas
  • 原文地址:https://www.cnblogs.com/Howfars/p/12289919.html
Copyright © 2011-2022 走看看