递归的定义
在数学与计算机科学中,递归是指在函数的定义中又调用函数自身的方法。如果p函数定义中调用p函数,称之为直接递归;如果p函数定义中调用q函数,而q函数中又调用p函数,称之为间接递归。这里主要的内容是关于直接递归的。
如果一个递归过程或递归函数中的递归调用语句是最后一条执行语句、那么称这种递归为尾递归。
通过递归可以简化问题的定义和求解过程。
例如:求解1~n的和(使用递归思想)。
图解:
代码如下:
public class Fun{
public static void main(String[] args) {
int result = fun(10);
System.out.println(result);
}
//递归求解
public static int fun(int n){
//f(1) = 1
if (n == 1){
return 1;
}
return fun(n - 1) + n;
}
}
递归算法通常是把一个大的复杂的问题层层转化成一个或多个与原问题相似的规模较小的的问题来求解,递归策略只需要少量的代码就可以描述出解题过程所需要的多次重复计算,大大的减少了算法的代码量。
一般来说,能够用递归解决问题的问题一个满足下面的三个条件:
- 需要解决的问题可以转化为一个或多个子问题来求解,而这些子问题的求解方法与问题完全相同,只是在数量规模上不同。
- 递归调用的次数必须是有限的
- 必须有结束递归的条件来终止递归(上述if(n == 1){return 1;}就是终止递归的条件)
斐波那契数列问题:
斐波那契数列为 1,1,2,3,5,8,13,21......
第一项和第二项均为1,其余项为前两项之和。设f(x)为斐波那契数列的第x项,则:
图解:
代码实现如下:
package 算法;
//斐波那契数列
public class recursionDemo03 {
public static void main(String[] args) {
int ret = f(5);
System.out.println(ret);
}
private static int f(int n) {
if (n == 1 || n == 2){
return 1;
}
return f(n - 1) + f(n - 2);
}
}
递归思路实现二分查找
package 算法;
public class RecursionDemo04 {
public static void main(String[] args) {
//二分查找 二分查找必须是一个有序数组
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15};
int ret = binarySearch(arr, 0, arr.length - 1, 13);
System.out.println(ret);
}
//在数组arr中 L~R区间内进行二分搜索查找key的角标
private static int binarySearch(int[] arr, int L, int R, int key) {
//如果元素不存在的时候
if (L > R){
return -1;
}
int mid = (L + R) / 2;
if (arr[mid] == key){
return mid;
}else if (arr[mid] > key){
return binarySearch(arr, L, mid - 1, key);
}else {
return binarySearch(arr, mid + 1, R, key);
}
}
}
递归算法设计的一般步骤:
用户在实际应用中要使用递归算法通常需要分析下面三个问题:
- 每一次递归调用在处理问题的规模上都应有所缩小。
- 相邻两次递归调用之间紧密的联系,前一次要为后一次递归调用做准备,通常是前一次递归的输出作为后一次递归调用的输入。
- 在问题的规模极小时必须直接给出问题的解而不再进行递归调用,因此每一次递归调用都是有条件的,无条件递归调用将会成为死循环而不能正常结束。
实例:递归遍历文件
代码如下:
package 算法;
import java.io.File;
//递归遍历文件
public class RecursionDemo02_1 {
public static void main(String[] args) {
//取出要遍历的文件,并将封装成对象
File file = new File("C:\\Users\\86133\\Desktop\\英语");
//调用遍历文件方法
traversal(file);
}
//实现遍历文件的方法
private static void traversal(File file) {
//将文件里的子文件封装成在File类型的数组中
File[] files = file.listFiles();
//如果数组为空,文件为空,直接返回
if (files.length == 0){
return;
}
//遍历数组
for (File f: files){
//如果为文件就打印
if (f.isFile()){
System.out.println(" "+f.getName());
//如果不是文件说明是文件目录,则将文件目录打印出来
}else{
System.out.println("【" + f.getName()+ " 】");
//打印出文件目录,再将文件目录传入traversal方法中遍历
traversal(f);
}
}
}
}