力扣——70.爬楼梯(简单难度)——学会将实例化的问题剖析为规律性问题

力扣——70.爬楼梯

  • 一、算法目录合集
    • 1.地址
    • 2.说明
  • 二、题目说明
    • 1.题干
    • 2.原地址
  • 三、实现步骤
    • 1.思路分析
      • 1.1.分析问题
      • 1.2.转化问题
      • 1.3.具体步骤
        • ① 特殊情况分析
        • ② 常规分析
    • 2.代码实现
      • 2.1 方法代码
      • 2.2 测试部分代码
      • 2.3 耗用资源情况
  • 四、官方题解
    • 1.原地址
    • 2.方法一——动态规划
      • 思路分析
      • 代码实现
      • 复杂度
    • 3.方法二——矩阵快速幂
      • 思路分析
      • 代码实现
      • 复杂度
    • 4.方法三——通项公式
      • 思路分析
      • 代码实现
      • 复杂度

一、算法目录合集

1.地址

   算法目录合集

2.说明

  该地址指向所有由本人自己所经历的算法习题(也有可能仅仅是一个入门的案例或者是经典案例),仅仅为我做过而且比较有意思的,也许还会有一些我自己想出来的,出于兴趣写在这里,具体格式我会在下面列明,总目录也会在这里出现,方便查阅以及自己进行复习回顾。

二、题目说明

1.题干

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

2.原地址

  70.爬楼梯

三、实现步骤

1.思路分析

  对于这种问题,大家的思维定式就是第一步先干啥,你说说你们为啥总在考虑先迈左脚还是先迈右脚呢,你第一步先弄啥,不是都得按照这么个规则往下走?那何不倒着进行,利用递归的思想去做题呢?

1.1.分析问题

  首先是分析一下,我们最后一步要干啥,要迈最后一步阶梯上楼,而这一步可能迈过一个台阶,也可能迈过两个台阶,那还愣着干啥,分情况讨论啊!第一种情况,最后一步迈一阶,那之前呢,之前就是n-1个台阶啊,你管他几步,先打包带走!第二种情况,最后一步迈两阶,那之前呢,之前就是n-2个台阶啊,也一起打包带走!这是不是就是总共的可能了?

1.2.转化问题

  也就是说:

爬第n阶楼梯的方法数量,等于 2 部分之和,即:
爬上 n−1 阶楼梯的方法数量。因为再爬1阶就能到第n阶
爬上 n−2 阶楼梯的方法数量,因为再爬2阶就能到第n阶
f(n) = f(n - 1) + f(n - 2)

  这是不是把这么一个实例化的问题转化为了简单的数学问题——斐波那契数列了?啥?你说你不懂斐波那契?给你个链接(斐波那契数列——兔子数列)自己瞅瞅去(想看看的点链接就行)。

1.3.具体步骤

① 特殊情况分析

  既然推理出了公式为 f(n) = f(n - 1) + f(n - 2),那么必然要考虑特殊情况,即 n=1 和 2 的时候,n = 1很简单,就是1步,n = 2也可以直接定义,就是2,也可以利用斐波那契的数列特征,为整个台阶创造出一个n = 0的情况,令 n= 0 时候,值为1 ,这里的n = 0,只是作为一个参数,方便后面计算,其实没有任何意义,因为题目要求了,n本身为正整数,所以这里他只是作为一个常量出现的。

if ( n == 1) {
     
	return 1;
}
int[] values = new int[n + 1];
values[0] = 1;
values[1] = 1;

② 常规分析

  常规步骤就是根据我们得出的核心公式==f(n) = f(n - 1) + f(n - 2)==来把所有的值退出来,并返回数列中的n就可以了

for (int i = 2; i <= n; i++) {
     
	values[i]=values[i-1]+values[i-2];
}
return values[n];

2.代码实现

2.1 方法代码

class Solution70 {
     
    public int climbStairs(int n) {
     
        if ( n == 1) {
     
            return 1;
        }
        int[] values = new int[n + 1];
        values[0] = 1;
        values[1] = 1;
        for (int i = 2; i <= n; i++) {
     
            values[i]=values[i-1]+values[i-2];
        }
        return values[n];
    }
}

2.2 测试部分代码

这里随便定义一个随便看看就好了

public class Test70Simple{
     
    public static void main(String[] args) {
     
        Solution70 s = new Solution70();
        System.out.println(s.climbStairs(6));
    }
}

2.3 耗用资源情况

力扣——70.爬楼梯(简单难度)——学会将实例化的问题剖析为规律性问题_第1张图片

四、官方题解

1.原地址

力扣官方答疑戳这里

2.方法一——动态规划

思路分析

直观想法
我们用 f(x)表示爬到第 x 级台阶的方案数,考虑最后一步可能跨了一级台阶,也可能跨了两级台阶,所以我们可以列出如下式子:
f(x) = f(x - 1) + f(x - 2)
f(x)=f(x−1)+f(x−2)
它意味着爬到第 xx 级台阶的方案数是爬到第 x−1 级台阶的方案数和爬到第 x - 2 级台阶的方案数的和。很好理解,因为每次只能爬 1 级或 2 级,所以 f(x) 只能从 f(x - 1)和 f(x - 2)转移过来,而这里要统计方案总数,我们就需要对这两项的贡献求和。
以上是动态规划的转移方程,下面我们来讨论边界条件。我们是从第 0 级开始爬的,所以从第 0 级爬到第 0 级我们可以看作只有一种方案,即 f(0) = 1;从第 0 级到第 1 级也只有一种方案,即爬一级,f(1) = 1。这两个作为边界条件就可以继续向后推导出第 n 级的正确结果。我们不妨写几项来验证一下,根据转移方程得到 f(2) = 2,f(3) = 3,f(4) = 5…我们把这些情况都枚举出来,发现计算的结果是正确的。
我们不难通过转移方程和边界条件给出一个时间复杂度和空间复杂度都是 O(n)的实现,但是由于这里的 f(x) 只和 f(x - 1)与 f(x - 2) 有关,所以我们可以用「滚动数组思想」把空间复杂度优化成 O(1)。下面的代码中给出的就是这种实现。

代码实现

class Solution {
     
    public int climbStairs(int n) {
     
        int p = 0, q = 0, r = 1;
        for (int i = 1; i <= n; ++i) {
     
            p = q; 
            q = r; 
            r = p + q;
        }
        return r;
    }
}

复杂度

  • 时间复杂度:
       循环执行 n 次,每次花费常数的时间代价,故渐进时间复杂度为 O(n)。
  • 空间复杂度:
      这里只用了常数个变量作为辅助空间,故渐进空间复杂度为 O(1)。

3.方法二——矩阵快速幂

思路分析

力扣——70.爬楼梯(简单难度)——学会将实例化的问题剖析为规律性问题_第2张图片

代码实现

public class Solution {
     
   public int climbStairs(int n) {
     
       int[][] q = {
     {
     1, 1}, {
     1, 0}};
       int[][] res = pow(q, n);
       return res[0][0];
   }
   public int[][] pow(int[][] a, int n) {
     
       int[][] ret = {
     {
     1, 0}, {
     0, 1}};
       while (n > 0) {
     
           if ((n & 1) == 1) {
     
               ret = multiply(ret, a);
           }
           n >>= 1;
           a = multiply(a, a);
       }
       return ret;
   }
   public int[][] multiply(int[][] a, int[][] b) {
     
       int[][] c = new int[2][2];
       for (int i = 0; i < 2; i++) {
     
           for (int j = 0; j < 2; j++) {
     
               c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
           }
       }
       return c;
   }
}

复杂度

  • 时间复杂度:
       同快速幂,O(log n)。
  • 空间复杂度:
       O(1) 。

4.方法三——通项公式

思路分析

力扣——70.爬楼梯(简单难度)——学会将实例化的问题剖析为规律性问题_第3张图片

代码实现

public class Solution {
     
    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)(fibn / sqrt5);
    }
}

复杂度

  • 时间复杂度:
       O(logn),pow 方法将会用去O(logn) 的时间。
  • 空间复杂度:
       O(1)。

你可能感兴趣的:(LeetCode,算法,java)