问题描述:
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
提示:
1 <= n <= 45
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/climbing-stairs
解题:
n=1 ==> 1
n=2 ==>1+1 / 2
n=3 ==>到达第三层只能从第一层或者第二层到达,所以1+2
n=4 ==>到达第四层只能从第二层或者第三层到达,所以2+3
依次类推:f(n) = f(n-1)+f(n-2)
1.拿到这道题,第一个冒出来的想法就是用暴力递归写。提交发现超时。
n=45时,2^45==>大概会计算这么多次
class Solution { public: int climbStairs(int n) { if(n == 1){ return 1; }else if(n == 2){ return 2; }else{ return climbStairs(n-1)+climbStairs(n-2); } };
2.记忆化递归,通过一个数组来记录已经计算过的结果,减少计算次数。
O(n)
class Solution { public: int *munber; int f(int n){ if(munber[n] > 0){ return munber[n]; } if(n == 1){ munber[1] = 1; }else if(n == 2){ munber[2] = 2; }else{ munber[n] = f(n-1)+f(n-2); } return munber[n]; } int climbStairs(int n) { munber = new int[n+1]; return f(n); delete []munber; //记得释放内存 } };
3.再分析,发现是一个典型的动态规划题(自底向上)
class Solution { public: int climbStairs(int n) { int dp[n+1]; dp[1] = 1; dp[2] = 2; for(int i=3;i<=n;i++){ dp[i] = dp[i-1]+dp[i-2]; } return dp[n]; } };
4.联想斐波那契数列,再优化
class Solution { public: int climbStairs(int n) { int f1=1; int f2=2; int fn; for(int i=3;i<=n;i++){ fn = f1+f2; f1 = f2; f2 = fn; } return fn; } };
5.公式解:Binet's Formula
这个解法我也不晓得,看了别人的写法。可以背下来,如果要推原理的话,需要用到矩阵的知识。
f(n) = f(n-1) + f(n-2)
那么可以得到特征方程:
x^2 = x^1 + x^0
方程的两个解为:x1=[1-5^(1/2)]/2 x2=[1+5^(1/2)]/2
不妨设通向公式为:
f(n) = c1x1^n+c2x2^n
由于f(1) = 1、f(2) = 1
c1 = 1/5^(1/2) c2 = -1/5(1^2)
所以:f(n) = 1/5^(1/2)*{[1-5^(1/2)]/2} ^n + -1/5(1^2)*{[1+5^(1/2)]/2} ^n
斐波那契第n项的结果为:
我们的初始条件: f(1)=1 f(2)=2
由前面的公式算的f(n+1)为我们题目的f(n)的解
class Solution { public: int climbStairs(int n) { double sqrt5 = sqrt(5); double fib = pow((1+sqrt5)/2,n+1)-pow((1-sqrt5)/2,n+1); return round(fib/sqrt5); } };