案例题目
-
编程实现参数n的阶乘并返回,所谓阶乘就是1累乘到n的结果。
-
方法一:递推
/* 编程实现累乘积的计算并打印 */ public class JieChengTest { // 1、自定义成员方法实现将参数n的阶乘计算出来并返回 // 1! = 1; 2! = 1 * 2; 3! = 1 * 2 * 3; ... n! = 1*2*3*...*n; int show(int n) { // 递推 int num = 1; for(int i = 1; i <= n; i ++) { num *= i; } return num; } public static void main(String[] args) { // 1、声明JieChengTest类型的引用指向该类型的对象 JieChengTest jct = new JieChengTest(); // 2、调用方法进行计算并打印 int res = jct.show(5); System.out.println("最终的计算结果是:" + res); // 120 } }
-
方法二:递归
/* 编程实现累乘积的计算并打印 */ public class JieChengTest { // 1、自定义成员方法实现将参数n的阶乘计算出来并返回 // 1! = 1; 2! = 1 * 2; 3! = 1 * 2 * 3; ... n! = 1*2*3*...*n; int show(int n) { // 递推的方式 /* int num = 1; for(int i = 1; i <= n; i ++) { num *= i; } return num; */ /* 5! = 5*4*3*2*1; 4! = 4*3*2*1; 3! = 3*2*1; 2! = 2*1; 1! = 1; 5! = 5*4!; 4! = 4*3!; 3! = 3*2!; 2! = 2*1!; 1! = 1; n! = n*(n-1); */ // 递归的方式 // 当 n 的结果为1时,则阶乘的结果就是1 /* if(1 == n){ return 1; }*/ if(1 == n) return 1; // 否则阶乘的结果就是n*(n-1)! return n * show(n-1); } public static void main(String[] args) { // 1、声明JieChengTest类型的引用指向该类型的对象 JieChengTest jct = new JieChengTest(); // 2、调用方法进行计算并打印 int res = jct.show(5); System.out.println("最终的计算结果是:" + res); // 120 } }
递归的基本概念
- 递归的本质就是指在方法体的内部直接或间接调用当前方法自身的形式。
使用递归的注意事项
- 使用递归必须有递归的规律以及退出条件。
- 使用递归必须是的问题简单化而不是复杂化。
- 若递归影响程序的执行性能,则使用递推取代之。
递归方式的原理分析
-
int show(int n) { // int n = 5; int n = 4; int n = 3; int n = 2; int n = 1; // 递推的方式 /* int num = 1; for(int i = 1; i <= n; i ++) { num *= i; } return num; */ /* 5! = 5*4*3*2*1; 4! = 4*3*2*1; 3! = 3*2*1; 2! = 2*1; 1! = 1; 5! = 5*4!; 4! = 4*3!; 3! = 3*2!; 2! = 2*1!; 1! = 1; n! = n*(n-1); */ // 递归的方式 // 当 n 的结果为1时,则阶乘的结果就是1 /* if(1 == n){ return 1; }*/ if(1 == n) return 1; // 否则阶乘的结果就是n*(n-1)! return n * show(n-1); // show(5) => return 5*show(4); => 120 // show(4) => return 4*show(3); => 24 // show(3) => return 3*show(2); => 6 // show(2) => return 2*show(1); => 2 // show(1) => return 1; => 1 }
费氏数列的递归实现------------------->此处递归会影响到程序的性能
-
编程实现费氏数列中第n项的数值并返回。
-
费氏数列:1 1 2 3 5 8 13 21
/* 编程实现费式数列的计算并打印 */ public class FeeTest { // 自定义成员方法实现费氏数列中第n项数值的计算并返回,n由参数指定 // 1 1 2 3 5 8 13 21 .... int show(int n) { // int n = 5; int n = 4; int n = 3; int n = 2; int n = 1; // 1、使用递归的方式进行计算 // 当 n = 1 或者 n = 2 时,结果是1 if(1 == n || 2 == n) return 1; // 否则结果是前两项的和 return show(n - 1) + show(n -2); // show(5) => return show(4) + show(3) => 5 // show(4) => return show(3) + show(2) => 3 // show(3) => return show(2) + show(1) => 2 // show(2) => return 1; => 1 // show(1) => return 1; => 1 } public static void main(String[] args) { // 1、声明FeeTest类型的引用指向该类型的对象 FeeTest ft = new FeeTest(); // 2、调用方法计算并打印 int res = ft.show(5); System.out.println("计算的结果是:" + res); // 5 } }
-
费氏数列的递推实现
-
/* 编程实现费式数列的计算并打印 */ public class FeeTest { // 自定义成员方法实现费氏数列中第n项数值的计算并返回,n由参数指定 // 1 1 2 3 5 8 13 21 .... int show(int n) { // int n = 5; int n = 4; int n = 3; int n = 2; int n = 1; // 1、使用递归的方式进行计算 /* // 当 n = 1 或者 n = 2 时,结果是1 if(1 == n || 2 == n) return 1; // 否则结果是前两项的和 return show(n - 1) + show(n -2); // show(5) => return show(4) + show(3) => 5 // show(4) => return show(3) + show(2) => 3 // show(3) => return show(2) + show(1) => 2 // show(2) => return 1; => 1 // show(1) => return 1; => 1 */ // 2、使用递推的方式进行计算----------------> 在很多场合中递归确实会简化代码,但是像这种不停的拆分的场合使用递归会影响性能,此时,我们应该使用递推 int ia = 1; int ib = 1; if(1 == n) { return ia; } if(2 == n) { return ib; } for(int i = 3; i <= n; i ++) { // n = 3时,才需要挪动。挪动总次数为 n - 2 次 int ic = ia + ib; ia = ib; ib = ic; } return ib; } public static void main(String[] args) { // 1、声明FeeTest类型的引用指向该类型的对象 FeeTest ft = new FeeTest(); // 2、调用方法计算并打印 int res = ft.show(55); System.out.println("计算的结果是:" + res); // 2144908973 } }
代码的拆分实现
-
功能类、封装类
/* 编程实现费式数列的计算并打印 功能类、封装类 */ public class Fee { // 自定义成员方法实现费氏数列中第n项数值的计算并返回,n由参数指定 // 1 1 2 3 5 8 13 21 .... int show(int n) { // int n = 5; int n = 4; int n = 3; int n = 2; int n = 1; // 1、使用递归的方式进行计算 /* // 当 n = 1 或者 n = 2 时,结果是1 if(1 == n || 2 == n) return 1; // 否则结果是前两项的和 return show(n - 1) + show(n -2); // show(5) => return show(4) + show(3) => 5 // show(4) => return show(3) + show(2) => 3 // show(3) => return show(2) + show(1) => 2 // show(2) => return 1; => 1 // show(1) => return 1; => 1 */ // 2、使用递推的方式进行计算----------------> 在很多场合中递归确实会简化代码,但是像这种不停的拆分的场合使用递归会影响性能,此时,我们应该使用递推 int ia = 1; int ib = 1; if(1 == n) { return ia; } if(2 == n) { return ib; } for(int i = 3; i <= n; i ++) { // n = 3时,才需要挪动。挪动总次数为 n - 2 次 int ic = ia + ib; ia = ib; ib = ic; } return ib; } }
-
测试类
/* 编程实现费氏数列的测试 测试类 */ public class FeeTest { public static void main(String[] args) { // 1、声明FeeTest类型的引用指向该类型的对象 Fee ft = new Fee(); // 2、调用方法计算并打印 int res = ft.show(55); System.out.println("计算的结果是:" + res); // 2144908973 } } /* 把代码拆分成两个文件的好处: 实现代码从物理上的划分,让结构更清晰,以后再看代码的时候Fee类就是一个类,类中包含了自己的成员。然后FeeTest类就是对功能类费氏数列类Fee进行测试的。 结构就很清晰,而且还可以实现代码的复用,以后别的类再想用到费氏数列的时候,就把这个Fee费氏数列类拿过去复用就行,不然总得带个main方法。 编译到第10行的时候就会默认从当前目录下去寻找一个叫做Fee的类,找到之后就顺带一起编译生成字节码文件了。 此处只是我们把代码拆分了一下,从结构上更清晰,从以后的维护上、复用上、重复使用上等等各个方面就更加有利一些。这也是编程规范。 */
-
注意:此处不可以直接用java FeeTest.java运行,因为java xxx.java只可以对该类操作,而Fee类是另外一个类,无法识别( 错误: 找不到符号),所以要使用javac先生成字节码文件再使用java运行。如果使用javac xxx.java编译生成字节码文件了,再使用java xxx.java去编译并执行会报错:找不到该文件路径。也不可以先使用javac编译封装类、功能类,再使用java xxx.java对测试类进行编制并且执行操作,这种操作也会导致错误。
-
综上所述,最好直接使用javac xxx.java对测试类进行编译,引起虚拟机顺带对功能类进行编译,然后再使用java xxx执行测试类。