《剑指offer》刷题——【递归循环】面试题10:斐波那契数列、青蛙跳台阶问题
一、题目描述
求斐波那契数列的第n项:
写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项,定义如下:
n=0, f(n)=0
n=1, f(n)=1
n>1, f(n)=f(n-1) + f(n-2)
二、解题思路
1. 书上常见解法,递归(效率低)
public long Fibonacci(int n){
if(n < = 0)
return 0;
if(n == 1)
return 1;
return Fibonacci(n-1)+Fibonacci(n-2);
}
上面方法递归方式如上图所示,由图可看出,在这棵树中有很多节点是重复的,而且重复的节点数会随着n的增大而急剧增加,计算量也会增大。用这种递归方法计算的时间复杂度是以n的指数方式递增,因此面试过程中不建议使用。
2. 改进递归,从下往上计算,把递归的算法用循环实现,时间复杂度O(n)
从下往上计算,首先根据 f(0) f(1) 算出 f(2),再根据 f(1) 和 f(2)算出 f(3)…以此类推就可以算出第n项
public class Solution {
public int Fibonacci(int n) {
if(n == 0)
return 0;
if(n == 1)
return 1;
int fibNMinusOne = 1;//第一个
int fibNMinusTwo = 0;//第二个
int fibN = 0;//和
//遍历
for(int i=2; i<=n; i++){
fibN = fibNMinusOne + fibNMinusTwo;
fibNMinusTwo = fibNMinusOne;
fibNMinusOne = fibN;
}
return fibN;
}
}
三、斐波那契数列应用——青蛙跳台阶问题
1. 题目描述
一只青蛙一次可以跳1级台阶,也可以跳上2级台阶,求该青蛙跳上一个n级的台阶总共有多少种跳法
2. 解题思路
(1)如果只有1级台阶,那显然只有一种跳法;
(2)如果有2级台阶,就2种跳法(一种是分两次跳,每次跳1级;另一种是一次跳2级);
(3)如果有n级台阶 f(n),当 n>2 时,第一次跳的时候就有两种不同的选择
- 第一次只跳1级,此时跳法数目等于后面剩下的 n-1级台阶跳法数目, f(n-1)
- 第一次跳2级,此时跳法数目等于后面剩下的 n-2 级台阶的跳法数目,f(n-2)
- 因此 f(n) = f(n-1) + f(n-2)
又上述分析,可看出,实际就是斐波那契数列。
3. 代码实现
public class Solution {
public int JumpFloor(int target) {
if(target == 1)
return 1;
if(target == 2)
return 2;
int MinusOne = 2;
int MinusTwo = 1;
int result = 0;
for(int i=3; i<=target; i++){
result = MinusOne + MinusTwo;
MinusTwo = MinusOne;
MinusOne = result;
}
return result;
}
}
四、变态跳台阶
1. 题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。。。。也可以跳上n级台阶,此时该青蛙跳上一个n级台阶总共有多少种跳法
2. 解题思路
因为n级台阶,第一步有n种跳法:跳1级、跳2级、到跳n级
跳1级,剩下n-1级,则剩下跳法是f(n-1)
跳2级,剩下n-2级,则剩下跳法是f(n-2)
。。。
又数学归纳法得:
所以f(n)=f(n-1)+f(n-2)+…+f(1)
因为f(n-1)=f(n-2)+f(n-3)+…+f(1)
所以f(n)=2*f(n-1)
f(n) = 2^2 f(n-2)
…
f(n) = 2^(n-1) f(1) = 2^(n-1)
3. 代码实现
public class Solution {
public int JumpFloorII(int target) {
return 1 << (target-1);//把一个数左移n位相当于把该数乘以2的n次方
}
}
五、矩阵覆盖
1. 题目描述
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
2. 解题思路
(1)把 2X8的覆盖方法记为 f(8);
(2)用第一个 2x1的小矩形去覆盖大矩形去覆盖大矩形的最左边时有两种选择:
- 横着放
- 竖着放
(3)当竖着放时,右边剩下 2x7的区域,记为 f(7)
(4)当横着放时,当横着放在左上角,左下角就必须横着放一个 2x1的小矩形,而在右边还剩下 2x6 的区域,f(6)
(5)因此,f(8) = f(7) + f(6)
由上述分析可知,仍是菲波那切数列
3. 代码实现
public class Solution {
public int RectCover(int target) {
if(target <= 2)
return target;
int minusOne = 2;
int minusTwo = 1;
int result = 0;
for(int i=3;i<=target; i++){
result = minusOne + minusTwo;
minusTwo = minusOne;
minusOne = result;
}
return result;
}
}