面试题 9:斐波那契数列
题目:
写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项。斐波那契数列的定义如下:
f
(
n
)
=
{
0
,
n
=
0
1
,
n
=
1
f
(
n
−
1
)
+
f
(
n
−
2
)
,
n
>
1
f(n)= \begin{cases} 0, & \text{$n = 0$}\\ 1, & \text{$n = 1$}\\ f(n - 1) + f(n - 2),& \text{$n > 1$} \end{cases}
f(n)=⎩⎪⎨⎪⎧0,1,f(n−1)+f(n−2),n=0n=1n>1
分析
首先想到的是利用递归来解,如:
public static int fibonacci(int n) {
if (n <=0 ) {
return 0;
} else if (n == 1) {
return 1;
} else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
该解法确实简单,很快就可以实现。让我们来分析一下:
我们以求解
f
(
10
)
f(10)
f(10)为例分析求解过程。想要求
f
(
10
)
f(10)
f(10)就的求
f
(
9
)
f(9)
f(9)和
f
(
8
)
f(8)
f(8),想要求
f
(
9
)
f(9)
f(9)需要先求
f
(
8
)
f(8)
f(8)和
f
(
7
)
f(7)
f(7),同样想要求
f
(
8
)
f(8)
f(8),又得先求
f
(
7
)
f(7)
f(7)和
f
(
6
)
f(6)
f(6) … 我们可以以树结构表达这种依赖关系。
不难发现,书中有很多节点重复,这意味着计算量会随着n的增大而急剧增大,事实上,用递归计算的时间复杂度是以n的指数递增的。我试了一下,当n为45时,就能很明显的发现获得结果需要等待一小会儿了。
改进
上面递归代码之所以慢是因为重复计算过多,当我们的递归从小往大计算时,第n次的结果总是前两次之后,而前两次刚好再前面两次已经计算过并记录了。
public static void main(String args[]) {
System.out.println(fibonacci1(1000));
}
public static int fibonacci1(int n) {
int[] result = {0, 1};
if (n < 2) {
return result[n];
}
int preNumberOne = 1;
int preNumberTwo = 0;
int number = 0;
for (int i = 2; i <= n; i++) {
number = preNumberOne + preNumberTwo;
preNumberTwo = preNumberOne;
preNumberOne = number;
}
return number;
}
斐波那契数列的运用:青蛙跳台阶
题目
一只青蛙可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个n级台阶总共有多少种跳法。
分析
首先考虑最简单的情况,如果只有一级台阶,青蛙第一次跳的时候,显然只有一种跳法。如果有2级,有两种跳法:一种是跳一级、一种是跳两级。
再看一般情况,假如青蛙跳n级台阶总共有 F ( n ) F{(n)} F(n) 种跳法,当n > 2 时,第一次有两种不同的选择:1. 只跳1级,此时跳法数据等于后面剩下的n - 1级台阶的跳法数目,即 $ F(n - 1) 。 2. 只 跳 2 级 , 此 时 跳 法 数 目 等 于 后 面 剩 下 的 n − 2 级 台 阶 的 跳 法 数 目 , 即 。2. 只跳2级,此时跳法数目等于后面剩下的n - 2级台阶的跳法数目,即 。2.只跳2级,此时跳法数目等于后面剩下的n−2级台阶的跳法数目,即 F(n - 2)$ 。因此n级台阶的不同跳法总数为 F ( n ) = F ( n − 1 ) + F ( n − 2 ) F(n) = F(n - 1) + F(n - 2) F(n)=F(n−1)+F(n−2)。不难看出,这就是一个斐波那契数列。
斐波那契数列的运用:矩形覆盖
题目
我们可以用2 x 1的小矩形横着或者竖着去覆盖更大的矩形。请问用8个 2 x 1的小矩形无重叠地覆盖一个 2 x 8的大巨星,总共有多少种方法?矩形如图:
分析
先把2 x 8的覆盖方法次数记为 F ( 8 ) F(8) F(8) , 用1x2的矩形第一次覆盖到2x8上有两种方式:
- 竖着盖在最左边,此时的覆盖次数有 F ( 7 ) F(7) F(7)。
- 横着覆盖最左上角, 此时覆盖次数记为 F ( 6 ) F(6) F(6)。
- 所以,对于2x8的矩形区域, F ( 8 ) = F ( 7 ) + F ( 6 ) F(8) = F(7) + F(6) F(8)=F(7)+F(6)
显然,又是一个斐波那契数列。
结语
斐波那契数列相关题目特征:
- 每个步骤有两种不同的操作。
- 有0和1两个解。
- 经过两个不同操作后,问题规模将会得到不同程度的降低。