题目连接:
题目:
解题思路:
1. 问题分析
设f(n)表示青蛙跳上n级台阶的跳法数。
当只有一个台阶时,即n = 1时,只有1中跳法;
当n = 2时,有2种跳法;
当n = 3 时,有3种跳法;
当n很大时,青蛙在最后一步跳到第n级台阶时,有两种情况:
一种是青蛙在第n-1个台阶跳一个台阶,那么青蛙完成前面n-1个台阶,就有f(n-1)种跳法,这是一个子问题。
另一种是青蛙在第n-2个台阶跳两个台阶到第n个台阶,那么青蛙完成前面n-2个台阶,就有f(n-2)种情况,这又是另外一个子问题。
两个子问题构成了最终问题的解,所以当n>=3时,青蛙就有f(n)=f(n-1)+f(n-2)种跳法。
上面的分析过程,其实我们用到了动态规划的方法,找到了状态转移方程,用数学方程表达如下:
仔细一看,这不就是传说中的著名的斐波那契数列,但是与斐波那契数列的还是有一点区别,斐波那契数列从0开始,f(0)=0,f(1)=1,f(2)=1。斐波那契数列(Fibonacci Sequence),又称黄金分割数列,因为当n趋于无穷大时,前一个数与后一个数的比值无限接近于黄金比例。
2.解题方法
(1)递归实现
有了初始状态和状态转移方程,那么编程实现求解就不难了,递归算法实现如下:
package kuaishou;
public class qingwataijie {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(JumpFloor(5));
}
private static int JumpFloor(int target) {
if(target == 1) {
return 1;
}else if(target == 2) {
return 2;
}else {
return JumpFloor(target-1)+JumpFloor(target-2);
}
}
}
但是我们都知道递归的时间复杂度和空间复杂度都很大,因此该递归算法的时间复杂度为O(),空间复杂度为O(n)。
不明白时间复杂度和空间复杂度怎么算的,可以看一下这篇文章。
Reference:青蛙跳台阶问题暨斐波那契数列
(2)迭代实现
能用递归实现的算法,当然我们也能用迭代方法实现。
递归算法的一个缺点就是重复计算。我们可以把已经得到的数列中间项保存起来,如果下次需要计算的时候,我们先查找一下,如果前面已经计算过就不用再重复计算了。
迭代法:首先根据f(0)和f(1)计算出f(2),在根据f(1)和f(2)计算出f(3)......依次类推就可以算出第n项了。
package kuaishou;
public class qingwataijie {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(JumpFloor(5));
}
private static int JumpFloor(int target) {
if(target == 1) {
return 1;
}else if (target == 2) {
return 2;
}else {
int res = 0;
int a = 1;
int b = 2;
for(int i=3; i<=target; i++) {
res = a + b;
a = b;
b = res;
}
return res;
}
}
}
迭代法实现的时间复杂度为O(n),空间复杂度为O(1)。
这个算法是时间复杂度最低的算法吗?当然不是,最快的应该是下面的矩阵法。
(3)矩阵法
根据上面的地推公式,我们可以得到:
有了这个公式,我们只需要求得矩阵,即可得到f(n)。现在的问题转为如何求矩阵的乘方。如果只是简单地从0开始循环,n次方需要n次运算,那其时间复杂度仍然是O(n),并不比前面的方法快。
第一种解法:矩阵拆分法,我们可以考虑乘方的如下性质:
从上面的公式中我们可以看出,想求得n次方,就要先求n/2次方,再把n/2次方的结果平方一下即可。
第二种解法:二进制法,我们可以类似地把a看做是二进制中的2,也就是说可以把矩阵的幂转换成二进制来表示。从而可以将n次幂拆解成长度为logn的二进制数来表示:7=111(二进制)。这也是快速求多阶矩阵的核心方法。
这里暂时还没有写出可以参考的代码,不过有很多可以参考的博客。