Leetcode爬楼梯算法与分析

@Leetcode爬楼梯

上楼梯经典问题,解法可谓是千奇百怪,但是几个最为人所熟知的方法还是依旧十分基础,说起来更是朗朗上口。不多说了,请看题干:

假设你正在爬楼梯。需要 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 阶

首先来到最为经典以及知名度最高的动态规划思路,请看代码:

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

在这里插入图片描述
毫不夸张地说,不管你在哪一本书中学习爬楼梯的问题,第一个介绍的必然是动态规划(俗称dp)算法。本题有一个非常关键的思路,希望大家能够发现:上第I级台阶的方法总共有两大种,第一种是从第(i-1)级台阶上跨一级,第二种则是从第(I-2)级台阶上跨两级,除此并无他法,由于这个上楼方法从(i-1)和(i-2)级各只有一种方法,那么上第i级台阶的总的方法和数就是上前两级台阶的方法总和数于是我们很快以此推出了动态规划的关系式,递推即可得到上面的代码。当然,要注意的是,使用动态规划法是无法在循环中判断最前两个台阶的,需要特判语句哦。

看完了上面的方法,细心的小白肯定发现了,诶,前两个数相加等于后两个数,这不是斐波那契数列吗?方法二就应运而生,请看代码:

class Solution {
public:
    int climbStairs(int n) {
        if(n==1)
            return 1;
        int first=1;
        int second=2;
        int third;
        for(int i=3;i<=n;i++){
                third=second+first;
                 first=second;
                 second=third;
        }
        return second;
    }
};

在这里插入图片描述
咋一看两份代码几乎一样嘛,实际上还是有不小的区别的。动态规划算法中创建了一个dp数组,存储了在抵达第n级台阶之前每级台阶的抵达时的方法,牺牲空间,超额完成了任务;斐波那契数列法则十分务实,每次计算都会更新数值,之前的数都会被覆盖,只能得到最后的答案,但是好处在开空间时只开了单个变量的空间并重复使用,节省了很多的空间。斐波那契数列数列的原理是第一个数+第二个数=第三个数,并且可以类推。时间复杂度上两者本应基本相同,但执行差异如此之大,也是有趣。

写到这里本已可以收笔,毕竟作为一个小白,会这两种方法,已经足够了,但只写这两种方法,你就只能是一个小白,于是笔者啥也不懂,鼓起勇气看了看大佬们的解法。来自大佬zzuliuwenbin的方法排列组合
链接:https://leetcode-cn.com/problems/two-sum/solution/pai-lie-zu-he-de-fang-shi-qiu-jie-by-zzuliuwenbin/
截取了这位老哥的思路,望各位看官好生鉴赏,请看方法三代码:

public class ClimbStairs {
    //排列组合公式计算,如果可以优化到o(1)时间内计算出来组合数,算法应该还算挺快
 public int C(int a,int b) {
  long t1 = 1, t2 = 1;
  for(int i=b,j=1;i>b-a;i--) {
   t1 = t1*i;t2 = t2*j;j++;
   if(t1%t2==0) {//计算到结尾再求结果会溢出,t1如果用int,在第43个测试用例时依旧溢出了
    t1 = t1/t2;
    t2 = 1;
   }
  }
  return (int)(t1/t2);
 }
 public int climbStairs(int n) {
  int sum = 0;
  for(int i=0;i<=n/2;i++) {
   if(i>(n-i)/2) {//利用组合公式的特点减少计算量
    sum = sum + C(n-2*i,n-i);
   }else {
    sum = sum + C(i,n-i);
   }
  }
        return sum;
    }
};

这个方法在大佬本人的描述中有2个核心思想:
1、n个台阶,一次走两个台阶的地方最多有n/2个;
2、假如某种可能里面包含i个一次走两个台阶的走法,那么这种走法就是在n-i步里面找到i个一次走俩台阶的,用组合公式C(i,n-i);
最后把所有i的取值的情况加起来,就能得到最终的方法个数。思路看上去比较简单,但代码编写并不容易,大佬多处注释也减少了小白们的压力,希望大家能有收获。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值