怒刷力扣(爬楼梯)

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

爬楼梯

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,祝你升职、加薪、不提桶!

目录
相关文章
|
存储 算法 索引
怒刷力扣(环形链表)
没想到曾经的寓言故事龟兔赛跑,还能应用在解循环的算法之中,今天是涨了见识了。
223 0
怒刷力扣(环形链表)
|
算法
怒刷力扣(出现一次的数字)
这个题不熟悉异或的同学可能找不到这个解题的方法。做了这么久的算法,发现很多算法题都能用到数学的方法进行计算,这样说可能不合适,算法本身就是数学的一种解题方法。还是感觉自身掌握的太少了。
111 1
怒刷力扣(出现一次的数字)
|
算法
怒刷力扣(验证回文串)
双指针算法,不是第一次用了,这个题使用双指针算法能提高近一半的效率,看见字符串就习惯性的对字符串进行处理。
78 0
怒刷力扣(验证回文串)
|
算法
怒刷力扣(买卖股票的最佳时机)
这个题本质就是分两步,第一步就是找到最低价即买入时机。找到买入时机之后则是对比利润找到卖出时机。解决这两步,答案就出来了。
85 1
|
算法
怒刷力扣(杨辉三角)
杨辉三角是在数学二项式中会遇到,在简单的算法题中出现的频率也是很高,不过确实是个简单的算法题,快来看看吧。
89 0
怒刷力扣(杨辉三角)
|
程序员
怒刷力扣( 路径总和)
粗心大意往往是BUG诞生的条件,那么细心必是解决BUG的良药,做个细心的不写BUG的程序员,从避免粗心大意开始。
911 2
怒刷力扣( 路径总和)
|
算法
怒刷力扣( 二叉树的最小深度)
这个题的坑就是没有左右子树的时候,这个节点才是叶子节点,这时候计算的结果才是根节点到叶子节点的深度。
88 2
怒刷力扣( 二叉树的最小深度)
怒刷力扣(平衡二叉树)
第一种方法类似二叉树的前序遍历,即根左右,先判断根节点的左右子树深度差,再分别判断左右子树。 而第二种方法类似二叉树的后序遍历,即左右根,先判断子树,再往上进行判断。
85 0
怒刷力扣(平衡二叉树)
怒刷力扣( 将有序数组转换为二叉搜索树)
将有序数组转换成平衡二叉树的关键就是看出转换的迭代和二分法的关系,能看出二分法,问题基本就已经解决了。
110 0
怒刷力扣( 将有序数组转换为二叉搜索树)
|
算法
怒刷力扣( 相同的树)
解决这个问题题有两种,第一种思路深度遍历更加直观明了,不会第二种的就学会第一种。能都掌握自然是更好。
50 1
怒刷力扣( 相同的树)