(LeetCode-70) 爬楼梯

题目

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

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

        注意:给定 n 是一个正整数。

        示例 1:

        输入: 2

        输出: 2

        解释: 有两种方法可以爬到楼顶。

        第1种方法.  1 阶 + 1 阶

        第2种方法.  2 阶

        示例 2:

        输入: 3

        输出: 3

        解释: 有三种方法可以爬到楼顶。

        第1种方法.  1 阶 + 1 阶 + 1 阶

        第2种方法.  1 阶 + 2 阶

        第3种方法.  2 阶 + 1 阶

分析和解答

方法1

我们仔细想下,实际上,可以根据第一步的走法把所有走法分为两类,第一类是第一步走了1个台阶,另一类是第一步走了2个台阶。

所以n个台阶的走法的个数就等于先走1阶后剩下的n-1个台阶的走法个数再加上先走2阶后剩下的n-2个台阶的走法个数。用公式表示就是︰

f(n) = f(n-1)+f(n-2)

这其实就是个递归公式,我们再来看下终止条件。当有一个台阶时,我们不需要再继续递归,就只有一种走法。所以f(1)=1。但是这个递归终止条件不够。

n=2时,f(2)=f(1)+f(0)。如果递归终止条件只有一个f(1)=1,那f(2)就无法求解了。所以除了f(⑴)=1这一个递归终止条件外,还要有f(0)=1,表示走0个台阶有一种走法,不过这样子有点滑稽。所以,我们可以把f(2)=2单独作为一种终止条件,表示走2个台阶,有两种走法,一步走完或者分两步来走。

所以,递归终止条件就是f(1)=1,f(2)=2。

综合在一起就是这样的:

这种方法的时间复杂度为O(n2)。

方法2

仔细分析我们上面的实现,最大的问题是什么?存在着大量的重复计算,我们以f(6)来分析一下:

可以看到在f(6)的求解过程中,f(3)和f(4)都被求解了多次,这个其实是没必要的,我们可以通过一个HashMap来保存已经求解过的f(k)。当递归调用到f(k)时,先看下是否已经求解过了。如果是,则直接从散列表中取值返回,不需要重复计算,这样就能避免刚讲的问题了。

这种方法的时间复杂度为O(n)。

方法3

在这个题目中,递归的解法是自顶向下,由最开始的顶层数字一层层分解直到最底层的f(1和f(2),再将结果又一层层累加上来。循环的解法则可以直接由底向上,从最底层的f(1和f(2),直接累加上来即可,而且从图中可以看到,上一个循环中计算出来的值刚好可以在下一个循环中使用。

不过一般来说,循环取代递归的解法在代码上要复杂一些,也比较难以理解一点。

这种方法的时间复杂度为O(n)。

package com.jiawei.recursive;

import java.util.HashMap;
import java.util.Map;

/**
 * ClassName: ClimbingStairs_70
 * Package: com.jiawei.recursive
 * Description: (LeetCode-70) 爬楼梯
 *  假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
 *  每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
 *  注意:给定 n 是一个正整数。
 *
 * @Author JiaWei-L
 * @Create 2024/1/13 23:20
 * @Version 1.0
 */
public class ClimbingStairs_70 {

    public static void main(String[] args) {

        ClimbingStairs_70 climbingStairs70 = new ClimbingStairs_70();

        /*递归的解法*/
        System.out.println(climbingStairs70.climbStairsWithRecursive(9));
        /*递归的解法,用HashMap存储中间计算结果*/
        System.out.println(climbingStairs70.climbStairs(9));
        /*循环的解法,自底向上累加*/
        System.out.println(climbingStairs70.climbStairsNoRecursive(9));

    }

    /*递归的解法*/
    /*f(n) = f(n - 1) + f(n - 2)*/
    public int climbStairsWithRecursive(int n) {

        if(n == 1)
            return 1;
        if(n == 2)
            return 2;

        return climbStairsWithRecursive(n-1) + climbStairsWithRecursive(n-2);
    }

    private final Map<Integer,Integer> storeMap = new HashMap<>();
//    private Map<Integer,Integer> storeMap = new HashMap<>();
    
    /*递归的解法,用HashMap存储中间计算结果*/
    public int climbStairs(int n) {

        if(n == 1)
            return 1;
        if(n == 2)
            return 2;

        if(null != storeMap.get(n))
            return storeMap.get(n);
        else{
            int result = climbStairs(n-1)+climbStairs(n-2);
            storeMap.put(n,result);
            return  result;
        }
    }

    /*循环的解法,自底向上累加*/
    public int climbStairsNoRecursive(int n) {

        if(n == 1)
            return 1;
        if(n == 2)
            return 2;

        int result = 0;
        int pre = 2;
        int prePre = 1;

        for (int i = 3; i <= n; ++i) {
            result = pre + prePre;
            prePre = pre;
            pre = result;
        }
        return result;
    }

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值