从 斐波那契数列 引出 青蛙跳台阶问题
斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、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*)
当求第n个数时,使用递归可以轻松得到以下代码:
public static int fibnacci(int n) {
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}
return fibnacci(n - 1) + fibnacci(n - 2);
}
例如当n = 4时,计算过程如下:
F(4) = F(3) + F(2)
= F(2) + F(1) + F(1) + F(0)
= F(1) + F(0) + F(1) + F(1) + F(0)
这个递归的时间复杂度:
递归时间复杂度 = 解决一个子问题时间*子问题个数
一个子问题时间 = F(n-1)+F(n-2),也就是一个加法的操作,所以复杂度是 O(1)
问题个数 = 递归树节点的总数,递归树的总结点 = 2^n-1,所以是复杂度O(2^n)
时间复杂度 = O(1) * O(2^n) = O(2^n)
可以看出时间复杂度是指数级别的,随着n的增大呈爆炸式增长。当n较大时就不免会有一些问题。
因此还有非递归的方法。从数列可以看出从第三个数开始,这个数的值是前两个数的和。
public static int fibnacci2(int n) {
if (n == 0) {
return 0;
}
if (n == 1 || n == 2) {
return 1;
}
int f1 = 1;
int f2 = 1;
int count = 3; // 从第3个数开始计算
while (count++ <= n) {
int temp = f1;
f1 = f2;
f2 = temp + f2;
}
return f2;
}
该方法时间复杂度:
解决一个子问题还是O(1)
子问题个数=树节点数=n
所以时间复杂度是O(n)
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)
找规律:
如果n=1,只有一种跳法,那就是1
如果n=2,那么有两种跳法,2,[1,1]
如果n=3,那么有三种跳法,[1,1,1],[1,2],[2,1]
如果n=4,那么有五种跳法,[1,1,1,1],[1,1,2],[1,2,1],[2,1,1],[2,2]
如果n=5,那么有八种跳法,[1,1,1,1,1],[1,1,1,2],[1,1,2,1],[1,2,1,1],[2,1,1,1],[2,2,1],[2,1,2],[1,2,2]
...
结果为1、2、3、5、8… ,即斐波那契数列
递归算法:
public static int jump(int n) {
if (n == 0)
return 0;
if (n <= 2)
return n;
return jump(n - 1) + jump(n - 2);
}
非递归算法:
public static int jump2(int n) {
if (n == 0)
return 0;
if (n == 1)
return 1;
if (n == 2)
return 2;
int n1 = 1;
int n2 = 2;
int count = 2;
while (count++ <= n) {
int tmp = n1;
n1 = n2;
n2 = tmp + n2;
}
return n2;
}
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法
f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-(n-1)) + f(n-n)
= f(0) + f(1) + f(2) + f(3) + ... + f(n-2)+f(n-1)
f(n-1) = f(0) + f(1)+f(2)+f(3) + ... + f((n-1)-1)
= f(0) + f(1) + f(2) + f(3) + ... + f(n-2)
so f(n)= 2*f(n-1)
递归算法:
public int jump3(int n) {
if (n <= 1) {
return 1;
} else {
return 2 * jump3(n - 1);
}
}
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上m级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-m)
f(n-1) = f(n-2) + f(n-3) + ... + f(n-m) + f(n-m-1)
so f(n)= 2f(n-1) - f(n-m-1)
递归算法:
public static int jump4(int n, int m) {
//当n大于m的时候是上面的公式
if (n > m) {
return 2 * jump4(n - 1, m) - jump4(n - 1 - m, m);
}
//当n小于等于m的时候就是和n级的相同了
if (n <= 1) {
return 1;
} else {
return 2 * jump4(n - 1, n);
}
}