递归算法基础 斐波那契问题几种实现方法 两个经典问题(兔子问题、奶牛问题)

递归算法是一种直接或间接调用自身函数或方法的算法,实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。

递归与for循环区别:

递归:可看作到楼顶取东西,从一楼开始爬,看,不是楼顶,继续爬,每层楼看上去都一样,执行过程也一样,等你到楼顶了,取到了你想要的东西,然后一层一层的退回来。

循环:可看作驴拉磨,无论驴拉多少次,都是在原地打转,位置不变,变化的只是磨盘里的东西。

为什么要用递归:我们要解决的很多问题本身的定义就是“递归”的,比如最常见的“树的遍历”,树本身就是一个递归的结构,如果你想要用程序来完成对它的遍历,最自然的方式也就是递归地把这个遍历过程描述出来,试想一下如果你偏要用循环的方式来完成树的遍历得多麻烦?代码长度也会增长很多很多;所以面对递归的问题,递归的解法是最自然的、最好理解的。

1、经典问题——斐波那契数列之兔子问题

斐波那契数列:兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?

依次类推可以列出下表:

经过月数 0 1 2 3 4 5 6 7 8 9 10 11 12
幼仔对数 1 0 1 1 2 3 5 8 13 21 34 55 89
成兔对数 0 1 1 2 3 5 8 13 21 34 55 89 144
总体对数 1 1 2 3 5 8 13 21 34 55 89 144 233

 幼仔对数=前月成兔对数   成兔对数=前月成兔对数+前月幼仔对数   总体对数=本月成兔对数+本月幼仔对数

 前面相邻两项之和,构成了后一项。

2、扩展问题——奶牛问题

一个农夫养了一头牛,三年后,这头牛每年会生出1头牛,生出来的牛三年后,又可以每年生出一头牛……问农夫10年后有多少头牛?n年呢?(用JAVA实现)

2.1 用递归的方法   (结果为28)

public class Cow {
	static int count = 1; //牛的数量
	private static void feedCow(int year,int age){
		year++; //第几年
		age++; // 对应牛的年龄
		if(year<=10){
			if(age>=3){
				count++;
				feedCow(year,0); // 新生牛
			}
			feedCow(year,age); //老牛长一岁
		}
	}
 
	public static void main(String[] args) {
		new Cow().feedCow(0, 0);
		System.out.println(count);
	}
}

思路:今年的牛的数目=去年的牛的数目+三年前牛的数目    即:f(n)=f(n-1)+f(n-3)

这里  f(n-2) 第一年 、 f(n-1) 第二年、  f(n) 第三年

int Fibonacci(int n){
if (n==1 || n==2 || n==3){
 return 1;
 }
 return Fibonacci(n-1)+Fibonacci(n-3);
 }

扩展:m年则得到  f(n)=f(n-1)+f(n-m)   又假如每头牛的寿命只有十年,则  f(n)=f(n-1)+f(n-3)-f(n-10)

3、递归优化

缓存原理:用类变量保存已经求出来的斐波那契结果,内存中这个结果如果已经有了,那么可以直接返回,不用再次计算。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 递归求斐波那契数列
 */
public class Recursion {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		BufferedReader strin=new BufferedReader(new InputStreamReader(System.in));
		int n=0;
		try {
			System.out.println("请输入50以内数字:");
			n=Integer.parseInt(strin.readLine());
		} catch (NumberFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Fibonacci.fibonacci(n));
		Fibonacci.printMap();
	}

}
/**
 * 工具类
 * 添加了缓存控制
 */
class Fibonacci{
	private static Map m=new HashMap();//用于保存n对应的斐波那契值
	  static long fibonacci(int n){
	   if(n<=1) return 1;
	   //缓存!如果存在直接返回
	   if(m.containsKey(n))
		   return (long)m.get(n);
	   long store=fibonacci(n-1)+fibonacci(n-2);
	   m.put(n, store);
	   return store;
	   } 
	  /**
	   * 对m的读取可能破坏了封装
	   * 打印出map
	   */
	  public static void printMap(){
		  Set keySet = m.keySet();
		  Iterator keys=keySet.iterator();
			while(keys.hasNext()){
				int temp=(int)keys.next();
				System.out.println(temp+":"+m.get(temp));
			}
		  
	  }
}

4、斐波那契几种实现方法

4.1非尾递归实现(书本上经常出现)  

public static long fibo1(long n){  
    if(n<2) return n;  
    return fibo1(n-1)+fibo1(n-2); //小心栈溢出  
}   

4.2 缓存实现

public static int LENGTH=30; //过大了就会占用过多空间  
public static long[] cache=new long[LENGTH];  
public static long fibo2(int n){  
    if(n<2) return n;  
    if(n>=LENGTH){  
        return fibo2(n-1)+fibo2(n-2);  
    }else if(cache[n]==0){  
        cache[n]=fibo2(n-1)+fibo2(n-2); //减少重复计算              
    }  
    return cache[n];  
}   

4.3 迭代实现  

public static long fibo3(long n){  
    if(n<2) return n;  
    long pre=1,prepre=1,ret=0;  
    for(int i=2;i<n;i++){  
        ret=pre+prepre;  
        prepre=pre;  
        pre=ret;  
    }  
    return ret;  
}   

4.4 尾递归实现  

public static long fibo4(int n){  
    if(n<2) return n;  
    return fibo4Helper(n, 1, 1, 3); //保持与非尾递归接口不变,是借助帮助方法实现尾递归的  
}  
private static long fibo4Helper(int n,long prepre,long pre,int begin){  
    if(n==begin) return pre+prepre;  
    return fibo4Helper(n, pre, prepre+pre, ++begin);    //这里相当于迭代实现for-loop的浓缩    
} 

    原文作者:递归算法
    原文地址: https://blog.csdn.net/wonfly2011/article/details/29353003
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞