剑指offer--9.斐波拉契数列

递归是在一个函数的内部调用这个函数自身,而循环是通过设置计算的初始值及终止条件,在一个范围内重复运算。

递归代码比较简洁,但也存在缺点,因为函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存中分配空间以保存参数、返回地址及临时变量,而往栈里压入数据和弹出数据都需要时间,且可能会造成调用栈溢出。

题目一:写一个函数,输入n,求斐波拉契数列的第n项,斐波拉契数列定义如下

f(n)=0,n=0

f(n)=1,n=1

f(n)=f(n-1)+f(n-2),n>1

思路:可以使用递归,但当n非常大时不适用,用循环解决

public class wr9Fibonacci {
//	递归
	public static long fibonacci(int n){
		if(n<=0){
			return 0;
		}
		if(n==1){
			return 1;
		}
		return fibonacci(n-1)+fibonacci(n-2);
	}
//	循环,时间复杂度为O(n)
	public static long fibonacci2(int n){
		long fn1=1;
		long fn2=1;
		if(n<=0){
			return 0;
		}
		if(n==1 || n==2){
			return 1;
		}
		for(;n>2;n--){
			long temp=fn1;
			fn1=fn1+fn2;
			fn2=temp;
//			不设置临时变量
//			fn1=fn1+fn2;
//			fn2=fn1-fn2;
		}
		return fn1;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println(fibonacci(10));
		System.out.println(fibonacci2(10));

	}

}

题目二:一只青蛙一次可以跳上1级台阶,也可以跳上2级,求该青蛙跳上一个n级台阶总共有多少种跳法

思路:如果只有1级,只有一种跳法;如果2级,有两种跳法,即一次跳2级,分两次跳每次跳1级,分析后发现实际就是斐波拉契数列

f(n)=1,n=1

f(n)=2,n=2

f(n)=f(n-1)+f(n-2),n>2
public static int jumpFloor(int target){
		int fn2=1;
		int fn1=2;
		if(target<=0){
			return 0;
		}
		if(target==1){
			return 1;
		}
		if(target==2){
			return 2;
		}
		for(;target>2;target--){
			fn1=fn1+fn2;
			fn2=fn1-fn2;
		}
		return fn1;
	}

题目三:一只青蛙一次可以跳上1级台阶,也可以跳上2级,,,也可以跳上n级,求该青蛙跳上一个n级台阶总共有多少种跳法

思路: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)=0,n=0

f(n)=1,n=1

f(n)=2*f(n-1),n>=2
public int jumpFloorMore(int target){
		if(target<=0){
			return 0;
		}
		int count=1;
		for(;target>1;target--){
			count=count*2;
		}
		return count;
	}
也可以用数学归纳法证明计算公式,代入直接计算
f(n)=2^(n-1)

扩展:我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形,问:用8个2*1的小矩形无重叠覆盖一个2*8的大矩形,共有多少种方法

思路:2*8的覆盖方法记为f(8),用第一个2*1小矩形去覆盖大矩形的最左边有两个选择,竖着或横着。竖着时,右边方法为f(7),横着时,右边为f(6),所以f(8)=f(7)+f(6)

public static int juxing(int target){
		if(target<=0){
			return 0;
		}
		if(target==1){
			return 1;
		}
		if(target==2){
			return 2;
		}
		int fn1=1;
		int fn2=2;
		for(;target>2;target--){
			fn2=fn2+fn1;
			fn1=fn2-fn1;
		}
		return fn2;
	}
总结:在遇到题目时,有时候我们可以通过数学逻辑推导找出规律,要善于发现,善于思考

猜你喜欢

转载自blog.csdn.net/autumn03/article/details/80100301