一、递归简介
- 使用递归就是使用递归方法(recursive method)编程,递归方法就是直接或间接调用自己的方法。
- 递归方法的特点
- 这些方法使用if-else或switch语句会导致不同的情况
- 一个或多个基础情况用来停止递归
- 每次递归都会简化原始问题,让它不断地接近基础情况,直到它变为基础情况
二、两个基础递归问题:
1.计算阶乘
阶乘的定义:
0!=1;
n!=n*(n-1)*...*2*1=n*(n-1)!; (n>0)
1 import java.util.Scanner; 2 public class ComputeFactorial{ 3 public static void main(String[] args){ 4 Scanner input=new Scanner(System.in); 5 System.out.print("Enter a nonnegtive integer:"); 6 int n=input.nextInt(); 7 System.out.println(n+"!="+factorial(n)); 8 } 9 private static int factorial(int n){ 10 if(n==0) 11 return 1; 12 else 13 return n*factorial(n-1); 14 } 15 }
2.计算斐波那契数
斐波那契数的定义:
fib(0)=0;
fib(1)=1;
fib(index)=fib(index-1)+fib(index-2); index>=2
1 import java.util.Scanner; 2 public class ComputeFibonacci{ 3 public static void main(String[] args){ 4 Scanner input=new Scanner(System.in); 5 System.out.print("Enter an index for Fobonacci number:"); 6 int n=input.nextInt(); 7 System.out.println(fib(n)); 8 } 9 private static int fib(int n){ 10 if(n==0) 11 return 0; 12 else if(n==1) 13 return 1; 14 else 15 return fib(n-1)+fib(n-2); 16 } 17 }
以上两个例子只是用来演示递归的概念,实际上用循环来实现效率更高,代码如下:
1 //阶乘 2 import java.util.Scanner; 3 public class ComputeFactorial{ 4 public static void main(String[] args){ 5 Scanner input=new Scanner(System.in); 6 System.out.print("Enter a nonnegtive integer:"); 7 int n=input.nextInt(); 8 int n0=n; 9 int s=1; 10 while(n>0) 11 { 12 s*=n--; 13 } 14 System.out.println(n0+"!="+ s); 15 } 16 }
1 //斐波那契数 2 import java.util.Scanner; 3 public class ComputeFibonacci{ 4 public static void main(String[] args){ 5 Scanner input=new Scanner(System.in); 6 System.out.print("Enter an index for Fobonacci number:"); 7 int n=input.nextInt(); 8 int f0=0,f1=1,currentFib=0; 9 for(int i=2;i<=n;i++){ 10 currentFib=f0+f1; 11 f0=f1; 12 f1=currentFib; 13 } 14 System.out.println(currentFib); 15 } 16 }
三、递归的辅助方法
1.递归的辅助方法
用递归的思路解决回文字符串问题:
1 import java.util.Scanner; 2 public class Palindrome{ 3 public static void main(String[] args){ 4 Scanner input=new Scanner(System.in); 5 System.out.print("Enter a string:"); 6 String text=input.nextLine(); 7 System.out.println(isPalindrome(text)); 8 } 9 private static boolean isPalindrome(String text){ 10 if(text.length()<=1) 11 return true; 12 else if(text.charAt(0)!=text.charAt(text.length()-1)) 13 return false; 14 else 15 return isPalindrome(text.substring(1,text.length()-1)); 16 } 17 18 }
由于每次递归时都要创建一个新的字符串,因此不够高效。为避免创建新字符串,可以使用low和high下标来表明子串的范围。
1 import java.util.Scanner; 2 public class Palindrome{ 3 public static void main(String[] args){ 4 Scanner input=new Scanner(System.in); 5 System.out.print("Enter a string:"); 6 String text=input.nextLine(); 7 System.out.println(isPalindrome(text)); 8 } 9 private static boolean isPalindrome(String text){ 10 return isPalindrome(text,0,text.length()-1); 11 } 12 private static boolean isPalindrome(String text,int low,int high){ 13 if(low>=high) 14 return true; 15 else if(text.charAt(low)!=text.charAt(high)) 16 return false; 17 else 18 return isPalindrome(text,low+1,high-1); 19 } 20 21 }
程序中定义了两个重载的isPalindrome方法,第一个方法将low和high的值传递给第二个方法,第二个方法采用递归调用,检测不断缩小的子串是否回文。在递归程序设计中定义第二个方法来接收附加的参数是一种常用的技巧,称为递归的辅助方法。
2.递归的辅助方法举例
选择排序
1 public class RecursiveSeletionSort{ 2 public static void sort(double[] list){ 3 sort(list,0,list.length-1); 4 } 5 public static void sort(double[] list,int low,int high){ 6 if(low<high){ 7 int indexOfMin=low; 8 double min=list[low]; 9 for(int i=low+1;i<=high;i++){ 10 if(min<list[i]){ 11 min=list[i]; 12 indexOfMin=i; 13 } 14 } 15 //Swap 16 list[indexOfMin]=list[low]; 17 list[low]=min; 18 19 sort(list,low+1,high); 20 } 21 } 22 }//只是算法,并非完整程序
二分查找
1 public class RecursiveBinarySearch{ 2 public static int binarySearch(int[] list,int key){ 3 int low=0; 4 int high=list.length-1; 5 return binarySearch(list,key,low,high); 6 } 7 public static int binarySearch(int[] list,int key,int low,int high){ 8 if(low>high) 9 return -low-1; 10 int mid=(low+high)/2; 11 if(key<list[mid]) 12 return binarySearch(list,key,low,mid-1); 13 else if(key=list[mid]) 14 return mid; 15 else 16 return binarySearch(list,key,mid+1,high); 17 } 18 }//只是算法,并非完整程序
四、尾递归
如果从递归调用返回时没有待定的操作要完成,这个递归就称为尾递归(tail recursive)。某些编译器会优化尾递归以减少桟空间。可以使用辅助参数将非尾递归转化为尾递归。
将阶乘的例子改为尾递归
五、更多例子
1.求出目录的大小
1 package book; 2 3 import java.io.File; 4 import java.util.Scanner; 5 6 public class DirectorySize{ 7 public static void main(String[] args){ 8 System.out.print("Enter a directory or a file:"); 9 Scanner input=new Scanner(System.in); 10 String directory=input.nextLine(); 11 12 System.out.println(getSize(new File(directory))+"bytes"); 13 14 input.close(); 15 } 16 public static long getSize(File file){ 17 long size=0; 18 if(file.isDirectory()){ 19 File[] files=file.listFiles(); 20 for(int i=0;i<files.length;i++){ 21 size+=getSize(files[i]); 22 } 23 } 24 else 25 size+=file.length(); 26 27 return size; 28 } 29 30 }
2.汉诺塔问题
1 import java.util.Scanner; 2 public class TowersOfHanoi{ 3 public static void main(String[] args){ 4 Scanner input=new Scanner(System.in); 5 System.out.print("Enter number of disks:"); 6 int n=input.nextInt(); 7 System.out.println("The moves are:"); 8 moveDisks(n,'A','B','C'); 9 } 10 private static void moveDisks(int n,char fromTower,char toTower,char auxTower){ 11 if(n==1) 12 System.out.println("Move disk "+n+" from "+fromTower+" to "+toTower); 13 else{ 14 moveDisks(n-1,fromTower,auxTower,toTower); 15 System.out.println("Move disk "+n+" from "+fromTower+" to "+toTower); 16 moveDisks(n-1,auxTower, toTower,fromTower); 17 18 } 19 20 } 21 22 }
3.八皇后问题
1 import java.awt.*; 2 import javax.swing.*; 3 public class EightQueens extends JApplet{ 4 /** 5 * 6 */ 7 private static final long serialVersionUID = 5660619297259886927L; 8 public static final int SIZE=8; 9 private int[] queens=new int[SIZE]; 10 11 public EightQueens(){ 12 search(0); 13 add(new ChessBord(),BorderLayout.CENTER); 14 } 15 16 private boolean isValid(int row,int cloumn){ 17 for(int i=1;i<=row;i++){ 18 if(queens[row-i]==cloumn 19 ||queens[row-i]==cloumn-1 20 ||queens[row-i]==cloumn+1) 21 22 return false; 23 } 24 return true; 25 } 26 27 private boolean search(int row){ 28 if(row==SIZE) 29 return true; 30 31 for(int cloumn=0;cloumn<SIZE;cloumn++){ 32 queens[row]=cloumn; 33 if(isValid(row,cloumn)&&search(row+1)) 34 return true; 35 } 36 37 return false; 38 } 39 40 class ChessBord extends JPanel{ 41 /** 42 * 43 */ 44 private static final long serialVersionUID = 4151757353625344600L; 45 private Image queenImage=new ImageIcon("image/queen.jpg").getImage(); 46 47 ChessBord(){ 48 this.setBorder(BorderFactory.createLineBorder(Color.BLACK,2)); 49 } 50 protected void paintComponent(Graphics g){ 51 super.paintComponent(g); 52 53 //Paint the queens 54 for(int i=0;i<SIZE;i++){ 55 int j=queens[i]; 56 g.drawImage(queenImage,j*getWidth()/SIZE,i*getHeight()/SIZE,getWidth()/SIZE,getHeight()/SIZE,this); 57 } 58 59 //Draw the horizontal and vertical lines 60 for(int i=0;i<SIZE;i++){ 61 g.drawLine(0,i*getHeight()/SIZE,getWidth(),i*getHeight()/SIZE); 62 g.drawLine(i*getWidth()/SIZE,0,i*getWidth()/SIZE,getHeight()); 63 } 64 65 } 66 } 67 }