1 题目再现
小明正在爬楼梯,一次要么上升1个台阶,要么上升2个台阶,问小明爬台阶个数为n的楼梯,一共有多少种方法?输入:楼梯台阶总数n,输出:爬n层台阶一共多少种方法?
2 解题思路
方法1:递归实现
经典递归问题,首先考虑n从1开始一共多少种方法(作为递归的出口)
n==1时,输出1,因为楼梯台阶数为1,小明一次要么上升1个台阶,要么上升2个台阶,所以只有一种方法爬到终点。
java
if(n==1) return 1;
n==2时,输出2,两种方法。方法1:一次爬1阶,一共爬两次 方法2:一次爬2阶,一共爬一次
java
if(n==2) return 2;
再回到要解决的问题,问的是爬n层台阶一共有多少种方法,只知道小明一次要么上升一个台阶或者两个台阶。如果顺着思考,就会觉得问题很困难,需要分好几种情况讨论(但是这也是一种方法叫迭代,下面接着讨论),就像树的分支一样,越分树枝越多。
如果逆着思考,也就是从最后一步看倒数第二步,只有两种可能,一种是从倒数第二步(n-2层台阶)跨两级台阶到达终点,另一种(n-1层台阶)跨一级台阶到达终点。
java
return solution(n-1)+solution(n-2);
每一层思考方式都是这样倒着推,内部逻辑函数一样,就可以调用函数自己再来一遍,也就是递归。但是每次递归都是再传入n-1和n-2再去求,需要有最终的出口,最终的出口就是n==1时返回1(一层台阶只有一种方法),n==2时返回2(两层台阶有两种方法)
递归实现完整代码Java
java
import java.util.Scanner;
public class Main {
public int Solution(int n){
if(n==1)return 1;
if(n==2)return 2;
return Solution(n-1)+Solution(n-2);
}
public static void main(String[] args) {
Main main=new Main();
Scanner scan=new Scanner(System.in);
int n =scan.nextInt();
System.out.println(main.Solution(n));
}
}
加入输入台阶数为4,求方法数正确为5,共5种爬该4层楼梯的方法
实际输出与预期一致

方法2:迭代实现
递归实现是从结果倒着推,利用函数自身的逻辑再循环内求直到源点n==1和n==2的情况。但是这种简单的逻辑几行代码往往里面会有潜在的资源浪费比如重复计算,仔细拆解就可发现如下图所示:
如图,在求爬5层台阶一共需要多少种方法的时候,一般先从结果倒着推,然后往下每一个子树内部处理逻辑都是和最大的从5层开始倒退的逻辑是一样的,这也就造成了每一部分函数递归又重新进行了一遍计算,就比如f(3)在从5倒着推的时候就已经计算一遍了,但是在4层倒着推的时候因为和兄弟节点中的f(3)独立,没法利用已经计算好的f(3),只能再重新计算一遍,就会造成资源浪费。
java
f(5)
/ \
f(4) f(3)
/ \ / \
f(3) f(2) f(2) f(1)
/ \
f(2) f(1)
那怎么才能不造成资源浪费?最简单的方法就是每个计算好的f(N)都存储起来,下次再用不用重复计算,直接把原先的值拿过来就行。这显然是递归这种从结果往前推,每一步都需要再通过源点这头的元素计算,无法实现的。
实现这种每个计算好的节点存储起来,只能通过从源点推结尾,一步一步的计算存储、计算存储。
也就是说:从已知的、最简单的情况开始,顺着往上算,而不是从 n 往回拆。
递归是:从上往下拆问题 → 遇到边界再回溯计算。迭代是:从下往上推结果 → 用前面算好的结果,直接算后面的。
因为每一步 f (i) 只依赖 f (i-1) 和 f (i-2),我们完全不需要把所有结果都存起来,只需要记住最近两个值,不断往后推就行。
迭代思路(顺着爬楼梯,从低到高)
我们已经明确:
- 台阶数为 1:只有 1 种走法,即 f (1) = 1
- 台阶数为 2:只有 2 种走法,即 f (2) = 2
台阶数为 3 时:最后一步要么从 2 级走 1 步,要么从 1 级走 2 步。所以:f (3) = f (2) + f (1)
台阶数为 4 时:f (4) = f (3) + f (2)
以此类推:f(n) = f(n-1) + f(n-2)
迭代就是:从 1、2 开始,一步步算出 3 → 4 → 5 → ... → n,每一个数只算一次,没有任何重复计算。
迭代实现完整代码Java
我们定义三个变量:
prePre:表示 f (n-2),一开始存 f (1) = 1pre:表示 f (n-1),一开始存 f (2) = 2res:表示当前算出的 f (n)
java
import java.util.Scanner;
public class Main {
public int Solution(int n) {
if (n == 1) return 1;
if (n == 2) return 2;
// 最开始:已知 f(1)=1,f(2)=2
int prePre = 1;
int pre = 2;
int res = 0;
// 从第3级开始,一直算到第n级
for (int i = 3; i <= n; i++) {
// 当前台阶方法数 = 前1个 + 前2个
res = prePre + pre;
// 变量往后滚动:为下一次循环做准备
prePre = pre;
pre = res;
}
return res;
}
public static void main(String[] args) {
Main main = new Main();
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
System.out.println(main.Solution(n));
scan.close();
}
}
关键三行代码解释(非常重要)
java
运行
res = prePre + pre; // 算出当前台阶的方法数
prePre = pre; // prePre 向后挪一位,变成原来的 pre
pre = res; // pre 向后挪一位,变成刚算出来的结果
-
第一次循环(i=3):prePre = f (1),pre = f (2)res = f (1)+f (2) = f (3)
-
第二次循环(i=4):prePre 变成 f (2)pre 变成 f (3)res = f (2)+f (3) = f (4)
每循环一次,就往上走一级台阶,只用到 3 个变量,不重复计算,效率极高