前言
写的是对leetcode《初级算法》爬楼梯题评论第一的算法的解析(❗是解析!解析!解析!❗算法不是我写的)
因为我第一次单看尾递归算法时比较懵,所以记录一下搞懂算法的过程,讲的应该算通俗易懂!
算法来自:作者:数据结构和算法
链接:https://leetcode.cn/leetbook/read/top-interview-questions-easy/xn854d/?discussion=DW6hWu
一、题目
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例:
输入:n = 3 输出:3
解释:有三种方法可以爬到楼顶。
- 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
提示:
1 <= n <= 45
二、算法及解析
1.斐波那契数列
先说一下斐波那契数
斐波那契数列指的是这样一个数列:
1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:
F(0)=0,F(1)=1,F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)
2.解决爬楼梯问题
一开始我想的是计算最多爬多少个二级台阶(n/2),然后0<=爬二级台阶的次数(t)<=n/2,然后计算有多种组合,但想想就知道太难了,所以pass掉(千万不要去想爬几个二级和一级)
以递归的思路去想这个问题:
假设n等于8,想去到第八个台阶,要么得在第七个阶梯,要么得在第六个阶梯,所以:
到达第八个阶梯=从第七个阶梯走一步上去+和第六个阶梯走两步上去
再说清楚一点:第七个阶梯 → 第八个阶梯,只能走一步,第六个阶梯 → 第八个阶,只能走两步。所以到达8个阶梯的总方法数=达到第7个阶梯的总方法+到第6个阶梯的总方法数
以此推到底可以知道:
当n等于3的时候,有f(3)=f(2)+f(1)
当n等于2的时候,共有2种跳法,f(2)=2
当n等于1:只有一种跳法,f(1)=1
再反推回去:
知道f(2)f(3)就能求出f(4),知道f(4)就能求出f(5),推到头就是结果。
递归算法
public static int climbStairs(int n) {
if (n <= 1)
return 1;
if (n < 3)
return n;
return climbStairs(n - 1) + climbStairs(n - 2);
}
这个算法在n较大的时候会出现超时问题,所以大佬给出了个尾递归算法
尾递归算法
public static int climbStairs(int n) {
return Fibonacci(n, 1, 1);
}
public static int Fibonacci(int n, int a, int b) {
if (n <= 1)
return b;
return Fibonacci(n - 1, b, a + b);
}
这里就要提到我们的斐波那契数列!
如何搞出计算该数列的尾递归算法,我们先画出两个指针,指针指向n=0…n,记录f(n)的值(蓝a,橙b):
根据F(0)=0,F(1)=1,F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*),可以看出应该记录前两个数
当要计算n=6时,我们需要移动指针
可以看出来移动后指针记录的数据发生了变化:蓝a=橙b,橙b=橙b+蓝a
再联系一下尾递归代码:
public static int Fibonacci(int n, int a, int b) {
if (n <= 1)
return b;
return Fibonacci(n - 1, b, a + b);
}
会发现当n>1时返回调用的函数参数:a=b,b=a+b
a,b分别记录目前已知的f(n-2)和f(n-1),然后移动更新a和b的值
知道f(n-2)和f(n-1)就知道f(n),更新a和b:b记录f(n),a记录f(n-1)
还不理解的可以自己画一画:
到这里也知道为什么一开始的参数是(n,1,1)了