今天我们来讲一讲递归函数,递归函数是一种隐式循环函数,在此过程中,它会重复调用自身所包含的方法。因此这种函数无需利用循环体进行控制。
递归:递归做为一种算法在程序设计语言中广泛应用。是指函数、过程、子程序在运行过程序中直接或间接调用自身而产生的重入现像。
程序调用自身的编程技巧称为递归( recursion)。
一个过程或函数在其定义或说明中又直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。
一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
注意:
- 递归就是在过程或函数里调用自身;
- 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
递归算法一般用于解决三类问题:
- 数据的定义是按递归定义的。(Fibonacci函数)
- 问题解法按递归算法实现。(回溯)
- 数据的结构形式是按递归定义的。(树的遍历,图的搜索)
递归的缺点:
递归算法解题的运行效率较低。
在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。
递归次数过多容易造成栈溢出等。
我们今天利用一个最基础的数列公式研究递归原理:
已知一个数列:f(n+2)=2*f(n+1)+f(n)(n为整数)
对于f(n)的算法,我们一般有两种计算方式:
一种是将n+2看成一个整体,那么f(n)=2*f(n-1)+f(n-2)
还有一种是直接对原等式进行调换得出f(n)=f(n+2)-2*f(n+1)
在起始两个元素已知的情况下,这两种算法都可以得出f(n)的数值,但前者的n为递增,而后者的n为递减。
这两个公式的代码如下:
/**
* @param n
* @return
*/
public static int fn01(int n) {
// TODO Auto-generated method stub
if (n == 0)
return 1;// 自行设定的数值
else if (n == 1)
return 4;// 自行设定的数值
else
return 2 * fn01(n - 1) + fn01(n - 2);
}
/**
* @param n
* @return
*/
public static int fn02(int n) {
// TODO Auto-generated method stub
if (n == 20)
return 1;// 自行设定的数值
else if (n == 21)
return 4;// 自行设定的数值
else
return fn02(n + 2) - 2 * fn02(n + 1);
}
在控制好两个起点数和运算公式的前提下,我们可以算出我们想要的f(n)值。main()程序代码如下:
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
for (int i = 0; i <= 10; i++) {
if (i == 0 || i == 1)
System.out.println("fn01(" + i + ") = " + fn01(i));
else {
int j = i - 1;
int k = i - 2;
System.out.println("fn01(" + i + ") = 2 * fn01(" + i + " - 1) + fn01(" + i + " - 2) = 2 * fn01(" + j
+ ") + fn01(" + k + ") = " + fn01(i));
}
}
for (int i = 21; i >= 0; i--) {
if (i == 21 || i == 20)
System.out.println("fn02(" + i + ") = " + fn02(i));
else {
int j = i + 2;
int k = i + 1;
System.out.println("fn02(" + i + ") = fn02(" + i + " + 2) - 2 * fn02(" + i + " + 1) = fn02(" + j
+ ") - 2 * fn02(" + k + ") = " + fn02(i));
// fn02(n + 2) - 2 * fn02(n + 1);
}
}
}
它们的运行结果分别为:
fn01(0) = 1
fn01(1) = 4
fn01(2) = 2 * fn01(2 - 1) + fn01(2 - 2) = 2 * fn01(1) + fn01(0) = 9
fn01(3) = 2 * fn01(3 - 1) + fn01(3 - 2) = 2 * fn01(2) + fn01(1) = 22
fn01(4) = 2 * fn01(4 - 1) + fn01(4 - 2) = 2 * fn01(3) + fn01(2) = 53
fn01(5) = 2 * fn01(5 - 1) + fn01(5 - 2) = 2 * fn01(4) + fn01(3) = 128
fn01(6) = 2 * fn01(6 - 1) + fn01(6 - 2) = 2 * fn01(5) + fn01(4) = 309
fn01(7) = 2 * fn01(7 - 1) + fn01(7 - 2) = 2 * fn01(6) + fn01(5) = 746
fn01(8) = 2 * fn01(8 - 1) + fn01(8 - 2) = 2 * fn01(7) + fn01(6) = 1801
fn01(9) = 2 * fn01(9 - 1) + fn01(9 - 2) = 2 * fn01(8) + fn01(7) = 4348
fn01(10) = 2 * fn01(10 - 1) + fn01(10 - 2) = 2 * fn01(9) + fn01(8) = 10497
fn02(21) = 4
fn02(20) = 1
fn02(19) = fn02(19 + 2) - 2 * fn02(19 + 1) = fn02(21) - 2 * fn02(20) = 2
fn02(18) = fn02(18 + 2) - 2 * fn02(18 + 1) = fn02(20) - 2 * fn02(19) = -3
fn02(17) = fn02(17 + 2) - 2 * fn02(17 + 1) = fn02(19) - 2 * fn02(18) = 8
fn02(16) = fn02(16 + 2) - 2 * fn02(16 + 1) = fn02(18) - 2 * fn02(17) = -19
fn02(15) = fn02(15 + 2) - 2 * fn02(15 + 1) = fn02(17) - 2 * fn02(16) = 46
fn02(14) = fn02(14 + 2) - 2 * fn02(14 + 1) = fn02(16) - 2 * fn02(15) = -111
fn02(13) = fn02(13 + 2) - 2 * fn02(13 + 1) = fn02(15) - 2 * fn02(14) = 268
fn02(12) = fn02(12 + 2) - 2 * fn02(12 + 1) = fn02(14) - 2 * fn02(13) = -647
fn02(11) = fn02(11 + 2) - 2 * fn02(11 + 1) = fn02(13) - 2 * fn02(12) = 1562
fn02(10) = fn02(10 + 2) - 2 * fn02(10 + 1) = fn02(12) - 2 * fn02(11) = -3771
fn02(9) = fn02(9 + 2) - 2 * fn02(9 + 1) = fn02(11) - 2 * fn02(10) = 9104
等于说在这个程序中,我们“啥都没做”,只是“以其人之道还治其人之身”:整个方法只有两个初始值和一个公式,但是在调用自身方法的过程中相当于公式中嵌套公式,进行了一个无休止的循环体(只有下限而无上限,或者只有上限而无下限),因此只要我们输入的n值存在于方法体中n的取值范围内,我们就能得出相应的结果。