怒刷力扣(爬楼梯)

简介: 斐波那契数列还会吗?初高中的求通项公式还会求吗?数学是不是白上了?如果你都还给老师了,那么赶紧找老师学回来吧。

爬楼梯

WangScaler: 一个用心创作的作者。

声明:才疏学浅,如有错误,恳请指正。

题目

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

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

初步分析

其实爬楼梯这个题很常见,可能数学找规律也做过。

  • 爬第一阶台阶的方法只有一种。爬1个台阶,共1个方法。
  • 爬第二个台阶的方法有两种。爬1+1个台阶或者爬2个台阶,共2个方法。
  • 爬第三个台阶的方法。有1+1+1个台阶或者1+2个台阶或者2+1个台阶,共3个方法。
  • 爬第四个台阶的方法。有1+1+1+1个台阶或者1+2+1个台阶或者1+1+2个台阶或者2+1+1个台阶或者2+2个台阶,共5个方法
  • .....
  • 总结下去,你会发现。f(3)=f(1)+f(2)=1+2=3。f(4)=f(3)+f(2)=3+2=5。
   public static int climbStairs(int n) {
        if(n==1||n==2){
            return n;
        }
        return climbStairs(n-1)+climbStairs(n-2);
    }

使用递归轻松实现。但是时间复杂度O(2^n)比较高。比如我们要算10级台阶,需要算九级台阶和八级台阶的个数。那么计算九级台阶的时候其实也会算八级台阶以及之前的台阶的个数,算完之后再去算八级台阶以及之前台阶的个数就是重复性操作。也就是算了两遍八级以及八级之前台阶的次数,那么我们能不能减少这个次数呢?

继续分析

既然要减少重复运算,那么必须要进行存储,存储之前计算过的台阶的值。

public static int climbStairs(int n) {
        int [] data = new int[n+1];
        return climb(n,data);
    }
    public static int climb(int n, int data[]){
        if(data[n]>0){
            return data[n];
        }
        if(n==1||n==2){
            data[n]=n;
            return n;
        }
        data[n] = climb(n-1,data)+climb(n-2,data);
        return data[n];
    }

这样我们只需要计算n级台阶,每一级台阶的次数即可,将时间复杂度降到O(n)。

那么从这里可以看到,我们每次递归都得回到第一级台阶计算,用数组的空间复杂度比较高,那么我们从头往后推不就行了吗?

    public static int climbStairs(int n) {
        int first  = 1;
        int next = 2;
        int model = 0;
        if(n==1||n==2){
            return n;
        }
        for (int i = 3; i < n ; i++) {
            model = first;
            first = next;
            next = model+first;
        }
        return first+next;
    }

这里有三个遍量。看起来不好理解,但是有个经典的游戏,将一个杯子的红水倒入另一个带蓝水杯子。倒完之后蓝水(不融合,也不会上浮)在上边。这时候我们需要借助第三个杯子,也就是三个变量中的model。剩下两个变量也很容易理解。f(3)时,first是1,next是2。那么f(4)是first就是2,next是1+2。这样空间复杂度就降低为O(1),只需要三个变量即可。

答案

其实上边的这个公式就是著名的斐波那契数列,也就是斐波那契数列。他的特征方程

image.png

求出x1和x2带入通解就得到下边的公式。

image.png

我们把这个公式转换成代码即可

public int climbStairs(int n) {
        double sqrt5 = Math.sqrt(5);
        double fibn = Math.pow((1 + sqrt5) / 2, n + 1) - Math.pow((1 - sqrt5) / 2, n + 1);
        return (int) Math.round(fibn / sqrt5);
    }

总结

答案中的数学公式,各位还会吗?这是初中还是高中的知识?不会的可以考虑考虑是否应该回去重学了。都还给老师了可不行。答案中还有个矩阵快速幂,建议大家去研究研究。今天先到此为止了。

都来了,点个赞再走呗!

关注WangScaler,祝你升职、加薪、不提桶!

目录
相关文章
|
存储 算法 索引
怒刷力扣(环形链表)
没想到曾经的寓言故事龟兔赛跑,还能应用在解循环的算法之中,今天是涨了见识了。
246 3
怒刷力扣(环形链表)
|
算法
怒刷力扣(出现一次的数字)
这个题不熟悉异或的同学可能找不到这个解题的方法。做了这么久的算法,发现很多算法题都能用到数学的方法进行计算,这样说可能不合适,算法本身就是数学的一种解题方法。还是感觉自身掌握的太少了。
159 4
怒刷力扣(出现一次的数字)
|
算法
怒刷力扣( 二叉树的最小深度)
这个题的坑就是没有左右子树的时候,这个节点才是叶子节点,这时候计算的结果才是根节点到叶子节点的深度。
187 4
怒刷力扣( 二叉树的最小深度)
|
程序员
怒刷力扣( 路径总和)
粗心大意往往是BUG诞生的条件,那么细心必是解决BUG的良药,做个细心的不写BUG的程序员,从避免粗心大意开始。
987 2
怒刷力扣( 路径总和)
|
算法
怒刷力扣( 相同的树)
解决这个问题题有两种,第一种思路深度遍历更加直观明了,不会第二种的就学会第一种。能都掌握自然是更好。
78 1
怒刷力扣( 相同的树)
|
算法
怒刷力扣(二叉树的中序遍历)
二叉树的中序遍历,前两种算法相对来说,比较容易理解,但是第三种,就需要自己思考思考了,想明白了其实也并不是很难。
122 1
怒刷力扣(二叉树的中序遍历)
怒刷力扣(删除排序链表中的重复元素)
单链表是我们在数据结构中非常常见的链表,那么最简单的删除链表元素你会吗?什么so easy?那下一篇?
149 1
怒刷力扣(删除排序链表中的重复元素)
|
存储
怒刷力扣(加一)
数字加一如果放到数组中会发生哪些奇奇怪怪得事情呢?那么接下来就一起看看数字放在数组中加一,怎么计算吧。
98 1
怒刷力扣(加一)
|
存储 算法
怒刷力扣(最大子数组和)
动态规划法试图仅仅解决每个子问题一次,从而减少计算量,一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。
116 1
怒刷力扣(最大子数组和)
|
算法
怒刷力扣(买卖股票的最佳时机)
这个题本质就是分两步,第一步就是找到最低价即买入时机。找到买入时机之后则是对比利润找到卖出时机。解决这两步,答案就出来了。
111 1