一、递归的定义
当求解一个问题时,若是通过求解与它具有同样解法的、相对简化的子问题而得到的,这就是递归。一个递归的求解问题必然包含终止递归的条件,当满足一定条件时就终止继续向下递归,从而使最小问题不通过递归而解决,然后再依次返回解决较大的问题,最后解决整个问题。解决递归问题的算法称为递归算法,在递归算法中需要根据递归条件直接或间接地调用算法本身,当满足终止条件时结束递归调用。当然对于一些简单问题的递归问题,很容易把它转换为循环问题来解决,从而使编写出的算法更为有效。
二、递归应用举例
1、采用递归算法求解正整数n的阶乘(n!).
由数学知识可知,n的阶乘的递归定义为:它等于n乘以n-1的阶乘,即n!=n*(n-1)!,并且规定1和0的阶乘为1。设函数f( n )=n!,则f( n ) 可表示为:
f(n)=1 (n=1或n=0)
f(n)=n*f(n-1) (n>1)
用Java语言编写出求解n!的递归函数为:
public static long f(int n)
{
if(n==1||n==0)
{
return 1;
}
else
{
return n*f(n-1);
}
}
2、编写一个算法输出n个二进制位的所有可能的编码值。
分析:每个二进制位取0和1两个值。根据题意,当n为1时有两个编码值0和1,当n为2时有4个编码值,依次为00、01、10、11,当n为3时有8个编码值,依次为000、001、010、011、100、101、110、111。总之,对于n个二进制位,所有可能的编码值为2^n个,每个编码值都有n位。
n个二进制位的2^n种不同的编码值可以写做2*2^(n-1),其中2^(n-1)个二进制位的所有编码值,每种包含n-1个二进制位。n个二进制位的每一种编码值是在n-1个二进制位的每个编码值的前面分别加上0或1而得到的结果,合起来正好是2*2^(n-1)=2^n种编码。由此可以看出它是一个递归的计算过程。
设n个二进制位用一个整型数组b[n]来表示,要得到b[0]~b[n-1]这n个二进制位的每一种可能的编码,则要首先在b[0]被置0的情况下得到b[1]~b[n-1]这n-1个二进制位的每一种可能的编码,然后再b[0]被置1的情况下得到b[1]~b[n-1]这n-1个二进制位的每一种可能的编码;同理,要得到b[1]~b[n-1]这n-1个二进制位的每一种可能的编码,则要首先在b[1]被置0的情况下得到b[2]~b[n-1]这n-2个二进制位的每一种可能的编码,然后在b[1]被置1的情况下得到b[2]~b[n-1]这n-2个二进制位的每一种可能的编码;依次类推,直到最后一个二进制位b[n-1]被置0后输出整个数组值和被置1后输出整个数组值为止。
下面是对b[0]~b[n-1]之间的n个二进制位输出其所有编码的递归算法,初始非递归调用时应把实参值0传送给形参k。
public static void coding(int b[],int k,int n)
{
if(k==n)
{
//终止递归,输出在b数组中排好的一个二进制数
for(int i=0;i<n;i++)
{
System.out.print(b[i]);
}
System.out.print(" ");
}
else
{
//把下标为k的元素置0后,从下标k-1起递归调用
b[k]=0;
coding(b,k+1,n);
//把下标为k的元素置1后,从下标k+1起递归调用
b[k]=1;
coding(b,k+1,n);
}
}
此算法在执行过程中需要被调用2^(n+1)-1次,其中有2^n次需要调用输出数组b中n个元素的值,所以算法的时间复杂度为O(n*2^n),该算法所使用的系统栈的最大深度为n+1,所以其空间复杂度我O(n),n为待编码的二进制位的个数。