1. 程式人生 > >遞迴演算法案例分析

遞迴演算法案例分析

一、遞迴練習(斐波那契數列)

  • 不死神兔
  • 故事得從西元1202年說起,話說有一位義大利青年,名叫斐波那契。
  • 在他的一部著作中提出了一個有趣的問題:假設一對剛出生的小兔一個月後就能長成大兔,再過一個月就能生下一對小兔,並且此後每個月都生一對小兔,一年內沒有發生死亡,
  • 問:一對剛出生的兔子,一年內繁殖成多少對兔子?
演算法分析:
  • 1 1 2 3 5 8 13
  • 第一個月一對小兔子 1
  • 第二個月一對大兔子 1
  • 第三個月一對大兔子生了一對小兔子 2
  • 第四個月一對大兔子生了一對小兔子
  • 一對小兔子長成大兔子 3
  • 第五個月兩對大兔子生兩對小兔子
  • 一對小兔子長成大兔子 5

我們首先可以用非遞迴的方法來做這個題目
private static void demo1() {
		// 方法一
		// 用陣列做不死神兔
		int[] arr = new int[12];
		arr[0] = 1;
		arr[1] = 1;
		// 遍歷陣列對其他元素賦值
		for (int i = 2; i < arr.length; i++) {
			arr[i] = arr[i - 2] + arr[i - 1];
		}
		// 如何獲取最後一個數,
		System.out.println(arr[arr.length - 1]);
	}


然後我們可以用第遞迴來做,
	// 第2種方法:用遞迴求斐波那契數列
	/*
	 * 分析: 1=fun(1) 1=fun(2) 2=fun(1)+fun(2) 3=fun(2)+fun(3)
	 */
	public static int fun(int num) {
		if (num == 1 || num == 2) {
			return 1;
		} else {
			return fun(num - 2) + fun(num - 1);
		}
	}


二、集合練習(約瑟夫環)
  • 幸運數字
/*
	 * 返回值型別為int
	 * 
	 */
	public static int getLuckNum(int num){
		//建立集合1到num的物件
		ArrayList<Integer>  list=new ArrayList<>();
		for(int i=1;i<=num;i++){
			list.add(i);    //儲存到集合中
		}
		int count=1;    //只要是3的倍數就remove掉
		for(int i=0;list.size()!=1;i++){  //只要集合中人數超過1,就要不斷的殺
			if(i==list.size()){
				i=0;         //如果i增長到集合最大的索引+1時,就重新歸0
			}
			if(count%3==0){
				list.remove(i--);
				
			}
			count++;
					
		}
		return list.get(0);
	}


三、
遞迴練習(1000的階乘所有零和尾部零的個數)
當然,如果我們直接使用以下方法來做的話是不可行的
/*
		 * int result=1; //這裡要從1開始,不能從0開始,因為任何數乘0都為0 
		 * for(int i=1;i<=1000;i++){
		 * 		result=result*i; 
		 * } 
		 * 	System.out.println(result);
		 * //此方法不能使用,因為這樣計算的值(1000的階乘)超出int的取值範圍了
		 */

所以我們需要這樣做,先去求出1000的階乘,然後再去統計有多少個0.
// 求出1000的階乘
		BigInteger bi1 = new BigInteger("1");
		for (int i = 1; i <= 1000; i++) {
			BigInteger bi2 = new BigInteger(i + "");// 轉換為字串型別的
			bi1 = bi1.multiply(bi2); // 將bi1與bi2相乘的結果賦值給bi1
		}

// 求出尾部所有的0
	private static void demo2(BigInteger bi1) {
		String str=bi1.toString();
		StringBuilder sb=new StringBuilder(str);
		str=sb.reverse().toString();  //鏈式程式設計
		int count=0;
		for(int i=0;i<str.length();i++){
			if('0'!=str.charAt(i)){
				break;
			}else{
				count++;
			}
		}
		System.out.println(count);
	}

	// 求出所有的0
	private static void demo1(BigInteger bi1) {
		String str = bi1.toString();// 獲取字串表現形式
		int count = 0;
		for (int i = 0; i < str.length(); i++) {
			if ('0' == str.charAt(i)) { // 如果字串中出現了0則計數器加1
				count++;
			}
		}
		System.out.println(count);
	}

那麼如果我們用遞迴來做的話,這個問題就會非常簡單了,
//求出1000的階乘所有零和尾部零的個數,用遞迴做
public class h {

	public static void main(String[] args) {
		System.out.println(fun(1000));

	}
	public static int fun(int num){
		
		if(num>0 && num<5){
			return 0;
		}else{
			return num/5 + fun(num/5);
		}
	}
}

File類遞迴練習(統計該資料夾大小)
需求:,從鍵盤接收一個資料夾路徑,統計該資料夾大小
</pre><pre name="code" class="java">/*
	 * 從鍵盤接收一個資料夾路徑
	 * 定義一個無限迴圈
	 * 將鍵盤錄入的結果儲存並封裝成File物件
	 * 對File物件判斷
	 * 將資料夾路徑物件返回
	 * 
	 * 
	 * 統計該資料夾大小
	 * 1、定義一個求和變數
	 * 2、獲取該資料夾下所有的檔案和資料夾listFiles();
	 * 3、遍歷陣列
	 * 4、判斷是檔案就計算大小並累加
	 * 5、判斷是資料夾就遞迴呼叫
	 * 
	 * 
	 */

	public static void main(String[] args){
		File dir=getDir();
		System.out.println(getFileLength(dir));
	}
	
	public static File getDir(){
		Scanner sc=new Scanner(System.in);
		System.out.println("請輸入一個資料夾路徑");
		while(true){
			String line=sc.nextLine();
			File dir=new File(line);
			if(!dir.exists()){
				System.out.println("目錄不存在");
			}else if(dir.isFile()){
				System.out.println();
			}else{
				return dir;
			}	
		}
	
	}
	
	public static long getFileLength(File dir){
		long len=0;
		File[] files=dir.listFiles();
		for(File file :files){
			if(file.isFile()){
				len=len+file.length();
			}else{
				len=len+getFileLength(file);
			}
		}
		return len;
	}



File類遞迴練習(刪除該資料夾)
需求:,從鍵盤接收一個資料夾路徑,刪除該資料夾

分析:
/*
	 * 1、獲取該資料夾下的所有檔案和資料夾
	 * 2、遍歷陣列
	 * 3、判斷是檔案直接刪除
	 * 4、如果是資料夾,遞迴呼叫
	 * 5、迴圈結束後,把控資料夾刪除
	 * 
	 */

	public static void main(String[] args) {
		File dir=a.getDir();
		deleteFile(dir);

	}
	public static void deleteFile(File dir){
		File[] subFiles=dir.listFiles();
		for(File file:subFiles){
			if(file.isFile()){
				file.delete();
			}else{
				deleteFile(file);
			}
		}
		//迴圈結束後把資料夾刪除掉
		dir.delete();
	}




File類遞迴練習(拷貝)
需求,從鍵盤接收兩個資料夾路徑,把其中一個資料夾中(包含內容)拷貝到另一個資料夾中

/*
	 * 1、在目標資料夾中建立原資料夾
	 * 2、獲取原資料夾中所有的檔案和資料夾,儲存在File陣列中
	 * 3、遍歷陣列
	 * 4、如果是檔案就用io流讀寫
	 * 5、如果是資料夾就遞迴呼叫
	 * 
	 */
	public static void main(String[] args) throws IOException {
		File src=a.getDir();
		File dest=a.getDir();
		if(src.equals(dest)){
			System.out.println("目錄資料夾是原始檔夾的子資料夾");
		}else{
			copy(src,dest);
		}
	}

	public static void copy(File src, File dest) throws IOException {
		File newDir=new File(dest,src.getName());
		newDir.mkdir();
		File[] subFiles=src.listFiles();
		for (File file : subFiles) {
			if(file.isFile()){
				BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
				BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(new File(newDir,file.getName())));
				int b;
				while((b=bis.read())!=-1){
					bos.write(b);
				}
				bis.close();
				bos.close();
			}else{
				//如果是資料夾就遞迴呼叫
				copy(file,newDir);
			}
		}
		
	}




File類遞迴練習(按層級列印)
* 要求:,從鍵盤接收一個資料夾路徑,把資料夾中的所有檔案以及資料夾的名字按層級列印,

/*
	 * 1、獲取所有檔案和資料夾,返回的File陣列
	 * 2、遍歷陣列
	 * 3、無論是檔案還是資料夾,都需要直接列印
	 * 4、如果是資料夾,遞迴呼叫
	 * 
	 */
	public static void main(String[] args) {
		File dir=a.getDir();
		printLev(dir,0);
		
	}

	public static void printLev(File dir,int level) {
		File[] subFiles=dir.listFiles();
		for (File file : subFiles) {
			for(int i=0;i<=level;i++){
				System.out.print("\t");
			}
			System.out.println(file);
			if(file.isDirectory()){
				printLev(file,level+1);
			}
		}
		
	}