题目描述
假设你正在爬楼梯。需要 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 阶
我的解法:暴力法运行超时
直接使用递归,会有大量的冗余运算,比如对一个特定的num,clmbStairs(num)会执行多次,时间复杂度o(2n)。
class Solution {
public:
int climbStairs(int n) {
if(n == 1 || n==0)
{
return 1;
}
return climbStairs(n-1) + climbStairs(n-2);
}
};
其它解法1:对暴力法的优化
对计算得到的每一个n结果存起来,在下次循环时的n如果是前面计算过的,就直接返回结果,避免重复运算,时间复杂度o(n)
class Solution {
public:
int climbStairs(int n)
{
int* p = new int[n+1];
return helper(n, p);
}
int helper(int n, int*p)
{
if(n == 0 || n == 1) return 1;
if(p[n] > 0) return p[n];
p[n] = helper(n-1, p) + helper(n-2, p);
return p[n];
}
};
其它解法2:动态规划
第一次做动态规划的问题,先记录以下关于动态规划的说明,如下所示。
动态规划英文 Dynamic Programming,是求解决策过程最优化的数学方法,后来沿用到了编程领域。
动态规划的大致思路是把一个复杂的问题转化成一个分阶段逐步递推的过程,从简单的初始状态一步一步递推,最终得到复杂问题的最优解。
动态规划解决问题的过程分为两步:
寻找状态转移方程
利用状态转移方程式自底向上求解问题
不难发现,这个问题可以被分解为一些包含最优子结构的子问题,即它的最优解可以从其子问题的最优解来有效地构建,因此我们可以使用动态规划来解决这一问题。
设方法数目为F,不难发现一个规律:令台阶数为i,则第一次可以跨一阶台阶,剩余i-1阶,即下一步求解i-1阶台阶的方案数;第一次也可以跨两阶,剩余i-2阶,即下一步求解i-2阶台阶的方案数。因此可得出状态转移方程:
F(N) = F(N-1) + F(N-2)
由此,代码如下:
class Solution {
public:
int climbStairs(int n) {
if(n == 1) return 1;
int* result_num = new int[n + 1];
result_num[1] = 1;
result_num[2] = 2;
for(int i = 3; i < n+1; i++)
{
result_num[i] = result_num[i-1] + result_num[i-2];
}
return result_num[n];
}
};
其它解法3:斐波那契数
观察动态规划解法,我们可以发现,没必要用数组存取每一层台阶的结果,只计算出来最后一级台阶即可。其实,第i个的结果就是第i个斐波那契数。
class Solution {
public:
int climbStairs(int n) {
if(n == 1) return 1;
int first = 1;
int second = 2;
for(int i = 3; i <= n; i++)
{
int third = first + second;
first = second;
second = third;
}
return second;
}
};
其它解法4:直接套斐波那契通项公式
F n = 1 / 5 ( ( 1 + 5 2 ) n + 1 − 1 − 5 2 ) n + 1 ) F_n= 1/ \sqrt{5} \Big(\big(\frac{1+\sqrt{5}}{2})^{n+1} - \frac{1-\sqrt{5}}{2}\big)^{n+1}\Big) Fn=1/5((21+5)n+1−21−5)n+1)
推理的过程请参见 知乎上的这个贴子,有了通项公式后,直接在常数级的时间复杂度范围内就可以求出结果了,代码如下:
class Solution {
public:
int climbStairs(int n) {
double root5 = sqrt(5);
return (1 / root5) * (pow((1 + root5) / 2, n + 1) - pow((1 - root5) / 2, n + 1));
}
};