算法目录合集
该地址指向所有由本人自己所经历的算法习题(也有可能仅仅是一个入门的案例或者是经典案例),仅仅为我做过而且比较有意思的,也许还会有一些我自己想出来的,出于兴趣写在这里,具体格式我会在下面列明,总目录也会在这里出现,方便查阅以及自己进行复习回顾。
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
70.爬楼梯
对于这种问题,大家的思维定式就是第一步先干啥,你说说你们为啥总在考虑先迈左脚还是先迈右脚呢,你第一步先弄啥,不是都得按照这么个规则往下走?那何不倒着进行,利用递归的思想去做题呢?
首先是分析一下,我们最后一步要干啥,要迈最后一步阶梯上楼,而这一步可能迈过一个台阶,也可能迈过两个台阶,那还愣着干啥,分情况讨论啊!第一种情况,最后一步迈一阶,那之前呢,之前就是n-1个台阶啊,你管他几步,先打包带走!第二种情况,最后一步迈两阶,那之前呢,之前就是n-2个台阶啊,也一起打包带走!这是不是就是总共的可能了?
也就是说:
爬第n阶楼梯的方法数量,等于 2 部分之和,即:
爬上 n−1 阶楼梯的方法数量。因为再爬1阶就能到第n阶
爬上 n−2 阶楼梯的方法数量,因为再爬2阶就能到第n阶
f(n) = f(n - 1) + f(n - 2)
这是不是把这么一个实例化的问题转化为了简单的数学问题——斐波那契数列了?啥?你说你不懂斐波那契?给你个链接(斐波那契数列——兔子数列)自己瞅瞅去(想看看的点链接就行)。
既然推理出了公式为 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];
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];
}
}
这里随便定义一个随便看看就好了
public class Test70Simple{
public static void main(String[] args) {
Solution70 s = new Solution70();
System.out.println(s.climbStairs(6));
}
}
力扣官方答疑戳这里
直观想法
我们用 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;
}
}
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;
}
}
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);
}
}