递归是在一个函数的内部调用这个函数自身,而循环是通过设置计算的初始值及终止条件,在一个范围内重复运算。
递归代码比较简洁,但也存在缺点,因为函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存中分配空间以保存参数、返回地址及临时变量,而往栈里压入数据和弹出数据都需要时间,且可能会造成调用栈溢出。
题目一:写一个函数,输入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)=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;
}
总结:在遇到题目时,有时候我们可以通过数学逻辑推导找出规律,要善于发现,善于思考