递归
递归是一种方法(函数)调用自己的编程技术。
1.三角数字
1,3,6,10,15,21,...
使用递归查找第N项是几
// triangle.java
// evaluates triangular numbers
// to run this program: C>java TriangleApp
import java.io.*;
class TriangleApp
{
static int theNumber;
public static void main(String[] args) throws IOException
{
System.out.print("Enter a number: ");
theNumber = getInt();
int theAnswer = triangle(theNumber);
System.out.println("Triangle="+theAnswer);
} // end main()
//-------------------------------------------------------------
public static int triangle(int n)
{
if(n==1)
return 1;
else
return ( n + triangle(n-1) );
}
//-------------------------------------------------------------
public static String getString() throws IOException
{
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();
return s;
}
//-------------------------------------------------------------
public static int getInt() throws IOException
{
String s = getString();
return Integer.parseInt(s);
}
//--------------------------------------------------------------
} // end class TriangleApp
2.变位字
变位字实际上就是高中学的排列,几个不同的字母共有几种不同的排列方法。如a.b.c共有8中排列方法。下面是其算法,该算法打印的结果是所有的排列结果。
// anagram.java
// creates anagrams
// to run this program: C>java AnagramApp
import java.io.*;
class AnagramApp
{
static int size;
static int count;
static char[] arrChar = new char[100];
//-----------------------------------------------------------
public static void main(String[] args) throws IOException
{
System.out.print("Enter a word: "); // get word
String input = getString();
size = input.length(); // find its size
count = 0;
for(int j=0; j<size; j++) // put it in array
arrChar[j] = input.charAt(j);
doAnagram(size); // anagram it
} // end main()
//-----------------------------------------------------------
public static void doAnagram(int newSize)
{
int limit;
if(newSize == 1) // if too small,
return; // go no further
for(int j=0; j<newSize; j++) // for each position,
{
doAnagram(newSize-1); // anagram remaining
if(newSize==2) // if innermost,
displayWord(); // display it
rotate(newSize); // rotate word
}
}
//-----------------------------------------------------------
// rotate left all chars from position to end
public static void rotate(int newSize)
{
int j;
int position = size - newSize;
char temp = arrChar[position]; // save first letter
for(j=position+1; j<size; j++) // shift others left
arrChar[j-1] = arrChar[j];
arrChar[j-1] = temp; // put first on right
}
//-----------------------------------------------------------
public static void displayWord()
{
if(count < 99)
System.out.print(" ");
if(count < 9)
System.out.print(" ");
System.out.print(++count + " ");
for(int j=0; j<size; j++)
System.out.print( arrChar[j] );
System.out.print(" ");
System.out.flush();
if(count%6 == 0)
System.out.println("");
}
//-----------------------------------------------------------
public static String getString() throws IOException
{
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();
return s;
}
//-----------------------------------------------------------
} // end class AnagramApp
3.递归的二分法查找
二分法查找效率高,因为它总是用最少的次数查到想要的值。
// binarySearch.java
// demonstrates recursive binary search
// to run this program: C>java BinarySearchApp
class ordArray
{
private long[] a; // ref to array a
private int nElems; // number of data items
//-----------------------------------------------------------
public ordArray(int max) // constructor
{
a = new long[max]; // create array
nElems = 0;
}
//-----------------------------------------------------------
public int size()
{ return nElems; }
//-----------------------------------------------------------
public int find(long searchKey)
{
return recFind(searchKey, 0, nElems-1);
}
//-----------------------------------------------------------
private int recFind(long searchKey, int lowerBound,
int upperBound)
{
int curIn;
curIn = (lowerBound + upperBound ) / 2;
if(a[curIn]==searchKey)
return curIn; // found it
else if(lowerBound > upperBound)
return nElems; // can't find it
else // divide range
{
if(a[curIn] < searchKey) // it's in upper half
return recFind(searchKey, curIn+1, upperBound);
else // it's in lower half
return recFind(searchKey, lowerBound, curIn-1);
} // end else divide range
} // end recFind()
//-----------------------------------------------------------
public void insert(long value) // put element into array
{
int j;
for(j=0; j<nElems; j++) // find where it goes
if(a[j] > value) // (linear search)
break;
for(int k=nElems; k>j; k--) // move bigger ones up
a[k] = a[k-1];
a[j] = value; // insert it
nElems++; // increment size
} // end insert()
//-----------------------------------------------------------
public void display() // displays array contents
{
for(int j=0; j<nElems; j++) // for each element,
System.out.print(a[j] + " "); // display it
System.out.println("");
}
//-----------------------------------------------------------
} // end class ordArray
class BinarySearchApp
{
public static void main(String[] args)
{
int maxSize = 100; // array size
ordArray arr; // reference to array
arr = new ordArray(maxSize); // create the array
arr.insert(72); // insert items
arr.insert(90);
arr.insert(45);
arr.insert(126);
arr.insert(54);
arr.insert(99);
arr.insert(144);
arr.insert(27);
arr.insert(135);
arr.insert(81);
arr.insert(18);
arr.insert(108);
arr.insert(9);
arr.insert(117);
arr.insert(63);
arr.insert(36);
arr.display(); // display array
int searchKey = 27; // search for item
if( arr.find(searchKey) != arr.size() )
System.out.println("Found " + searchKey);
else
System.out.println("Can't find " + searchKey);
} // end main()
} // end class BinarySearchApp
4.汉诺塔问题(Hanoi)
这是个经典的游戏,下面的A、B、C分别代表左、中、右的三根柱子,1、2、3分别代表从上往下也就是从小到大的盘子。
// towers.java
// solves Towers of Hanoi puzzle
// to run this program: C>java TowersApp
class TowersApp
{
static int nDisks = 3;
public static void main(String[] args)
{
doTowers(nDisks, 'A', 'B', 'C');
}
//-----------------------------------------------------------
public static void doTowers(int topN,
char src, char inter, char dest)
{
if(topN==1)
System.out.println("Disk 1 from " + src + " to "+ dest);
else
{
doTowers(topN-1, src, dest, inter); // src to inter
System.out.println("Disk " + topN + // move bottom
" from " + src + " to "+ dest);
doTowers(topN-1, inter, src, dest); // inter to dest
}
}
//-------------------------------------------------------------
} // end class TowersApp
5.归并排序
归并排序的效率要高于简单排序(冒泡、选择和插入排序),简单排序时间复杂度都是O(N2),而归并只需要O(N*logN),归并排序的一个缺点是它需要在存储器中有另一个大小等于被排序的数据项树木的数组,如果初始数组几乎占满整个存储器,那么归并排序将不能工作,但是如果有足够的空间,归并排序会是一个很好的选择。
下面是一个执行归并排序的java程序,它并不是递归程序,只是为了理解归并排序的插曲。
// merge.java
// demonstrates merging two arrays into a third
// to run this program: C>java MergeApp
class MergeApp
{
public static void main(String[] args)
{
int[] arrayA = {23, 47, 81, 95};
int[] arrayB = {7, 14, 39, 55, 62, 74};
int[] arrayC = new int[10];
merge(arrayA, 4, arrayB, 6, arrayC);
display(arrayC, 10);
} // end main()
//-----------------------------------------------------------
// merge A and B into C
public static void merge( int[] arrayA, int sizeA,
int[] arrayB, int sizeB,
int[] arrayC )
{
int aDex=0, bDex=0, cDex=0;
while(aDex < sizeA && bDex < sizeB) // neither array empty
if( arrayA[aDex] < arrayB[bDex] )
arrayC[cDex++] = arrayA[aDex++];
else
arrayC[cDex++] = arrayB[bDex++];
while(aDex < sizeA) // arrayB is empty,
arrayC[cDex++] = arrayA[aDex++]; // but arrayA isn't
while(bDex < sizeB) // arrayA is empty,
arrayC[cDex++] = arrayB[bDex++]; // but arrayB isn't
} // end merge()
//-----------------------------------------------------------
// display array
public static void display(int[] theArray, int size)
{
for(int j=0; j<size; j++)
System.out.print(theArray[j] + " ");
System.out.println("");
}
//-----------------------------------------------------------
} // end class MergeApp
使用了递归的归并排序完整版。
// mergeSort.java
// demonstrates recursive merge sort
// to run this program: C>java MergeSortApp
class DArray
{
private long[] theArray; // ref to array theArray
private int nElems; // number of data items
//-----------------------------------------------------------
public DArray(int max) // constructor
{
theArray = new long[max]; // create array
nElems = 0;
}
//-----------------------------------------------------------
public void insert(long value) // put element into array
{
theArray[nElems] = value; // insert it
nElems++; // increment size
}
//-----------------------------------------------------------
public void display() // displays array contents
{
for(int j=0; j<nElems; j++) // for each element,
System.out.print(theArray[j] + " "); // display it
System.out.println("");
}
//-----------------------------------------------------------
public void mergeSort() // called by main()
{ // provides workspace
long[] workSpace = new long[nElems];
recMergeSort(workSpace, 0, nElems-1);
}
//-----------------------------------------------------------
private void recMergeSort(long[] workSpace, int lowerBound,
int upperBound)
{
if(lowerBound == upperBound) // if range is 1,
return; // no use sorting
else
{ // find midpoint
int mid = (lowerBound+upperBound) / 2;
// sort low half
recMergeSort(workSpace, lowerBound, mid);
// sort high half
recMergeSort(workSpace, mid+1, upperBound);
// merge them
merge(workSpace, lowerBound, mid+1, upperBound);
} // end else
} // end recMergeSort()
//-----------------------------------------------------------
private void merge(long[] workSpace, int lowPtr,
int highPtr, int upperBound)
{
int j = 0; // workspace index
int lowerBound = lowPtr;
int mid = highPtr-1;
int n = upperBound-lowerBound+1; // # of items
while(lowPtr <= mid && highPtr <= upperBound)
if( theArray[lowPtr] < theArray[highPtr] )
workSpace[j++] = theArray[lowPtr++];
else
workSpace[j++] = theArray[highPtr++];
while(lowPtr <= mid)
workSpace[j++] = theArray[lowPtr++];
while(highPtr <= upperBound)
workSpace[j++] = theArray[highPtr++];
for(j=0; j<n; j++)
theArray[lowerBound+j] = workSpace[j];
} // end merge()
//-----------------------------------------------------------
} // end class DArray
class MergeSortApp
{
public static void main(String[] args)
{
int maxSize = 100; // array size
DArray arr; // reference to array
arr = new DArray(maxSize); // create the array
arr.insert(64); // insert items
arr.insert(21);
arr.insert(33);
arr.insert(70);
arr.insert(12);
arr.insert(85);
arr.insert(44);
arr.insert(3);
arr.insert(99);
arr.insert(0);
arr.insert(108);
arr.insert(36);
arr.display(); // display items
arr.mergeSort(); // merge sort the array
arr.display(); // display items again
} // end main()
} // end class MergeSortApp
6.消除递归
有一些算法趋向于用递归的方法,另一些则不是,其实递归的效率并不高,人们用他们只是他们更加简洁,所以有时候需要把递归的算法转换成非递归的,这种转换经常会用到栈。
事实上,大部分的编译器都是使用栈来实现递归的,当调用一个方法的时候,编译器会把这个方法的所有参数及返回地址(这个方法返回时控制到达的地方)都压入栈,然后把控制转移给这个方法,当这个方法返回的时候,这些值退栈,参数消失了,并且控制权重新回到返回地址处。
下面是一个转换的算法,实现的是前面的triangle.java,三角数字。
// stackTriangle.java
// evaluates triangular numbers, stack replaces recursion
// to run this program: C>java StackTriangleApp
import java.io.*; // for I/O
class Params // parameters to save on stack
{
public int n;
public int returnAddress;
public Params(int nn, int ra)
{
n=nn;
returnAddress=ra;
}
} // end class Params
class StackX
{
private int maxSize; // size of StackX array
private Params[] stackArray;
private int top; // top of stack
//--------------------------------------------------------------
public StackX(int s) // constructor
{
maxSize = s; // set array size
stackArray = new Params[maxSize]; // create array
top = -1; // no items yet
}
//--------------------------------------------------------------
public void push(Params p) // put item on top of stack
{
stackArray[++top] = p; // increment top, insert item
}
//--------------------------------------------------------------
public Params pop() // take item from top of stack
{
return stackArray[top--]; // access item, decrement top
}
//--------------------------------------------------------------
public Params peek() // peek at top of stack
{
return stackArray[top];
}
//--------------------------------------------------------------
} // end class StackX
class StackTriangleApp
{
static int theNumber;
static int theAnswer;
static StackX theStack;
static int codePart;
static Params theseParams;
//-------------------------------------------------------------
public static void main(String[] args) throws IOException
{
System.out.print("Enter a number: ");
theNumber = getInt();
recTriangle();
System.out.println("Triangle="+theAnswer);
} // end main()
//-------------------------------------------------------------
public static void recTriangle()
{
theStack = new StackX(10000);
codePart = 1;
while( step() == false) // call step() until it's true
; // null statement
}
//-------------------------------------------------------------
public static boolean step()
{
switch(codePart)
{
case 1: // initial call
theseParams = new Params(theNumber, 6);
theStack.push(theseParams);
codePart = 2;
break;
case 2: // method entry
theseParams = theStack.peek();
if(theseParams.n == 1) // test
{
theAnswer = 1;
codePart = 5; // exit
}
else
codePart = 3; // recursive call
break;
case 3: // method call
Params newParams = new Params(theseParams.n - 1, 4);
theStack.push(newParams);
codePart = 2; // go enter method
break;
case 4: // calculation
theseParams = theStack.peek();
theAnswer = theAnswer + theseParams.n;
codePart = 5;
break;
case 5: // method exit
theseParams = theStack.peek();
codePart = theseParams.returnAddress; // (4 or 6)
theStack.pop();
break;
case 6: // return point
return true;
} // end switch
return false;
} // end triangle
//-------------------------------------------------------------
public static String getString() throws IOException
{
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();
return s;
}
//-------------------------------------------------------------
public static int getInt() throws IOException
{
String s = getString();
return Integer.parseInt(s);
}
//--------------------------------------------------------------
} // end class StackTriangleApp
以上算法多少是系统的把一个递归程序转换成了使用栈的程序,这表明对于任意一个递归程序都有可能做出这种转换,然而在实践中,我们从一开始就不往递归想,所以一开始就用基于栈的方法,而不是从递归转换,这样就可以精简上面的程序,去掉了swith语句,使代码更有效率。
以下是更有效率的代码。
// stackTriangle2.java
// evaluates triangular numbers, stack replaces recursion
// to run this program: C>java StackTriangle2App
import java.io.*; // for I/O
class StackX
{
private int maxSize; // size of stack array
private int[] stackArray;
private int top; // top of stack
//--------------------------------------------------------------
public StackX(int s) // constructor
{
maxSize = s;
stackArray = new int[maxSize];
top = -1;
}
//--------------------------------------------------------------
public void push(int p) // put item on top of stack
{ stackArray[++top] = p; }
//--------------------------------------------------------------
public int pop() // take item from top of stack
{ return stackArray[top--]; }
//--------------------------------------------------------------
public int peek() // peek at top of stack
{ return stackArray[top]; }
//--------------------------------------------------------------
public boolean isEmpty() // true if stack is empty
{ return (top == -1); }
//--------------------------------------------------------------
} // end class StackX
class StackTriangle2App
{
static int theNumber;
static int theAnswer;
static StackX theStack;
public static void main(String[] args) throws IOException
{
System.out.print("Enter a number: ");
System.out.flush();
theNumber = getInt();
stackTriangle();
System.out.println("Triangle="+theAnswer);
} // end main()
//-------------------------------------------------------------
public static void stackTriangle()
{
theStack = new StackX(10000); // make a stack
theAnswer = 0; // initialize answer
while(theNumber > 0) // until n is 1,
{
theStack.push(theNumber); // push value
--theNumber; // decrement value
}
while( !theStack.isEmpty() ) // until stack empty,
{
int newN = theStack.pop(); // pop value,
theAnswer += newN; // add to answer
}
}
//-------------------------------------------------------------
public static String getString() throws IOException
{
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();
return s;
}
//-------------------------------------------------------------
public static int getInt() throws IOException
{
String s = getString();
return Integer.parseInt(s);
}
//--------------------------------------------------------------
} // end class StackTriangle2App
总之,递归能简化代码,但是逻辑上比较复杂,效率也并不高。