LeetCode70. 爬楼梯(看了那么多还不懂,那就记一下吧)


70.爬楼梯 LeetCode(看我,如果学了还没看懂的话)

参考链接1

参考链接2

参考链接3

如果这些还没看懂,看下面吧

思路

这是一道很无聊的题,评论里有一个列了45个case的值得我的反思,真正解决实际问题的时候,不一定需要华丽的算法,为了求快和稳定性,如果实际项目需要,这种45个case的最值得推荐!!!??

? 哈哈哈,扯远了,这45个case也是得先通过某种算法求出来的,那么具体什么算法当然值得推敲一下呀!!

emmm其实推敲他的解法还是很无聊的,因为所有的题都只是围绕着如何快速求斐波那契数列上了,这道题的题解随着n的增大是一个斐波那契数列

n=1 1

n=2 2

n=3 3

n=4 5

也就是说:
$$
f(1)=1,f(2)=2\
f(n)=f(n-1)+f(n-2)
$$

解法一:简单递归

时间复杂度:O(2n)

空间复杂度:O(n)

首先最先想到的显然是用递归去做:

f(int n){
    if(n==1) return 1;
    if(n==2) return 2;
    else return f(n)+f(n-1);
}

那个衡量一个算法,肯定是先看一下他的时间复杂度和空间复杂度,这个递归的空间复杂度和时间复杂度都是很高的,如何感性的体会一下呢?

转载侵删

时间复杂度不难看出,需要遍历所有的结点,才能求出f(5),这里的每个结点,都是实实在在的的跑了一个递归函数,需要时间的!!程序是按顺序逐条执行的!一共是2的n次方量级。

空间复杂度:我一开始没绕明白,以为这些结点都存在与内存中,其实这些结点都存在过,但是不是同时存在的!!!,在f5被调用的时候,由于f5 = f4 + f3,所以程序需要按顺序计算,,,f4和f3,但是f4在f3前面,所以先算f4。其实f4还有好长的路要走,所以暂时f3没有被运行。所以瞬时栈里最多存在红线这么多条程序然后f1退了f0上还是5个程序段。f2因此求出来了,程序其实卡在求f3的程序段里,这时候栈里只有三段了。。。。以此类推。所以空间复杂度最大是n。


解法二:临时数组

时间复杂度:O(n)

空间复杂度:O(1)

递归这种算法,很多时候的计算是不必要的!!!!(忽然明白了大黑书里的一句话)。比方说计算f5的时候需要计算f4和f3,这是实打实递归地去算。然而计算f4 需要计算f3 和f2 ,这里的f3 又重新计算了一遍,也就是按照递归的办法,一层层算的!很是麻烦呀!!

其实细心的小伙伴很快就能看出来,下图。。。。

img

力扣上的图,,,,转载转载,侵删侵删;

可以放一个数组,每次计算完了,把结果放在数组里,再保留上一次数组里的最大值,就好啦

class Solution {
    public int climbStairs(int n) {
        int a = 1;
        int b = 0;
        int temp = 0;
        for(int i = 1;i<=n;i++){
            temp = a;
            a = a+b;
            b=temp;
        }
        return a;
    }
}

不难看出,计算f(n)的时间复杂度是n,只需要计算O(n)次

空间复杂度是O(1)

这个不难。。。。


解法三 矩阵快速幂

时间复杂度:O(lgn)

空间复杂度:O(1)

? 如果说一个算法的时间复杂度是O(n)已经相当不错了,那么这个解法是O(lgn)听起来是不是相当刺激呢??!!!

? 那这里涉及到一个新的名字就是快速幂

快速幂

? 具体的就去百度一下吧!!!

我大概说一下:如果计算a的10次方

a*a*a*a*a*a*a*a*a*a*a

? 抛给计算机的就是9次乘法运算,这里没有开辟任何多余的空间

但是比方说前五个a连乘的结果被记录下来的话,令其为t

那么计算机就这么计算

a*a*a*a*a*t

? 也就是5次乘法计算就好啦(算不算以空间换时间呢 挺值的我感觉)

那么这5个a的连乘其实也可以按照这种方法继续细化下去,以减少计算乘法的次数

令 m=(a*a)

? 那就是

(a*a)*m*a*t 变成了4次计算

这里不难写出他的算法,本质上是一个二分

好了就介绍这些

那么问题来了,这个算法确实能以lgn的速度减少运算次数,但是我这个题是加法运算,跟乘法 和幂有什么关系呢?

这里就涉及到数学的魔法了。。根据力扣官方的讲解,特征方程这些就不说了。我忘记了,,,

实际上我只需要计算这个M的n次方就可以了呀,这里的(f0 = 0 )

那不就是幂运算吗,n次方的幂运算可以变成lgn的时间复杂度,wow好特么惊奇,,,,

下边就是我按照官方给的写法写出来的代码(基本一模一样,但是我真的是自己凭感觉撸出来的)

(矩阵的乘法,和正常的乘法不是一样的,虽然矩阵的一次乘法运算要比数字的乘法运算复杂很多,需要算很多次乘法运算才能完成一次矩阵的乘法运算,但是当n足够大的时候,这个快速幂运算lgn的时间复杂度便十分牛掰了)

上代码,不多说了

class Solution {
    //主程序
    public int climbStairs(int n) {
        int[][] div = new int[][]{{1,1},{1,0}};
        int[][] res = pow(div,n);//自定义了矩阵的幂运算pow,它按照快速幂的算法
        return res[0][0];

    }
    
	//这里是pow快速幂算法的核心,不难但是不好想通
    int[][] pow(int[][] div, int n){
        //作为一个存结果的变量,他是单位矩阵,因为它第一做乘法的时候,需要满足a*1=a
        int[][] eyes = new int[][]{{1,0},{0,1}};
  		//核心
        while(n>0){
            if((n&1)==1){
                eyes = multi(div,eyes);
            }
            n>>=1;
            div = multi(div,div);       
        }
   //解释一下核心,我总纠结于他的边界条件,就是最后是奇数还是偶数,但它这样的格式,使得无论如何,n都要从1变成零,如果最后一次是2 那么移位之后是1 然后变成0;如果是1 那么直接出结果,所以无论如何,`div = multi(div,div);`都被执行了!!!
   //核心也就是,是偶数n/=2,是基数就n--,那么无论怎么样都会进行了x个--,y个/=2,
        //在每个--后边都跟着一个  暂存结果*=原矩阵,
        //在每个/=2后边div都做  div*=div 也就是自平方(快速幂的根源)
   //所以完全合理呀一点也不差 
     
    return eyes;
    }  
    
//这是矩阵乘法的函数,很简单,任何一个学过编程的人都能写出来。不注释了。。。
    int[][] multi(int[][] a, int[][] b){
            int[][] res = new int[2][2];

            for(int i=0;i<2;i++){
                for(int j =0;j<2;j++){
                    res[i][j] = a[i][0]*b[0][j]+a[i][1]*b[1][j];
                }
            }
   return res;
    } 

}

这里的快速幂算法的程序代码值得看明白,挺好玩,。。

哎一天啥也没干就做了两道中等题,课题组汇报耽误一天白天时间,我好慌,毕业论文咋办!!我还没搞呢,,研二了都,,,,55555
------------恢复内容结束------------