更多算法可以参考 算法汇总传送门
递归
应用场景
- 各种数学问题:8皇后问题、汉诺塔、阶乘问题、迷宫问题(回溯)、球和篮子的问题
- 各种算法中也会使用到递归,比如快排、归并排序、二分查找、分治算法等
- 将用栈解决的问题用递归解决,代码比较简洁
递归概念
简单来说:递归就是方法调用自身,每次调用传入不同的变量。递归有助于编程者解决复杂的问题,同时可以让代码变的简洁。
简单举例
打印问题
逻辑分析
代码实现
package com.algorithm.recursion;
public class RecursionTest {
public static void main(String[] args) {
//通过打印问题,回顾递归调用机制
test(4);
}
public static void test(int n){
if(n > 2){
test(n -1);
}
System.out.println("n=" + n);
}
}
运行结果
阶乘问题
代码实现
//阶乘问题
public static int factorial(int n){
if(n == 1){
return 1;
}else {
return factorial(n - 1) * n;
}
}
思路分析
System.out.println(factorial(4));
运行时:
n == 4 factorial(3) * 4
n == 3 factorial(2) * 3
n == 2 factorial(1) * 2
n == 1 1
最终变为 123*4 = 24
运行结果
递归重要规则
- 执行一个方法时,就会创建一个新的受保护的独立的空间(栈空间)
- 方法的局部变量是独立的,不会相互影响,比如上边例子中的n变量
- 如果方法中使用的是引用类型的变量,就会共享该引用类型的数据:比如迷宫问题中的数组
- 递归必须向退出递归的条件逼近,否则就是无线递归,出现StackOverflowError,死归了
- 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕
迷宫问题
代码实现
package com.algorithm.recursion;
public class MiGong {
public static void main(String[] args) {
//先创建一个二维数组,模拟迷宫地图
int[][] map = new int[8][7];
//使用1表示迷宫的墙壁,小球不能过
//先把上下全部置为1
for (int i = 0; i < 7; i++) {
map[0][i] = 1;
map[7][i] = 1;
}
//把左右全部置为1
for (int i = 0; i < 8; i++) {
map[i][0] = 1;
map[i][6] = 1;
}
//生成挡板
map[3][1] = 1;
map[3][2] = 1;
// map[1][2] = 1;
// map[2][2] = 1;
//输出地图
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
// setWay(map,1,1);
setWay2(map,1,1);
System.out.println("走过之后的地图:");
//输出地图
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
}
//使用递归回溯来给小球找路
//说明
//1. map表示地图
//2. i j表示地图从哪个位置开始(1,1)
//3. 如果小球能到map[6][5],则说明通路找到
//4. 约定:当map[i][j]为0时表示该点没有走过;
// 为1表示墙,不能走;
// 为2表示通路可以走;
// 为3表示该位置已经走过,但是走不通
//5. 在走迷宫时需要确定一个策略(方法),比如:先走下,走不通再走右-->上-->左
//如果该点走不通再回溯
/**
* @param map 表示地图
* @param i 表示从哪个位置开始
* @param j
* @return 如果找到通路,就返回true,否则返回false
*/
public static boolean setWay(int[][] map, int i, int j) {
if (map[6][5] == 2) { //道路已经走通
return true;
} else {
if (map[i][j] == 0) { //表示这个点还没有走过
map[i][j] = 2; //假定这个点能够走通
if (setWay(map, i + 1, j)) { //向下走
return true;
} else if (setWay(map, i, j + 1)) { //向右走
return true;
} else if (setWay(map, i - 1, j)) { //向上走
return true;
} else if (setWay(map, i, j - 1)) { //向左走
return true;
} else { //说明这个点走不通
map[i][j] = 3;
return false;
}
} else { //如果map[i][j] != 0 ,可能是 1 2 3
//1 表示墙壁不能走
//3 表示此路不通
//2 表示已经探明可以走。不必再走
return false;
}
}
}
//修改找路的策略 改成上->右->下->左
public static boolean setWay2(int[][] map, int i, int j) {
if (map[6][5] == 2) { //道路已经走通
return true;
} else {
if (map[i][j] == 0) { //表示这个点还没有走过
map[i][j] = 2; //假定这个点能够走通
if (setWay2(map, i - 1, j)) { //向上走
return true;
}else if (setWay2(map, i, j + 1)) { //向右走
return true;
}else if (setWay2(map, i + 1, j)) { //向下走
return true;
}else if (setWay2(map, i, j - 1)) { //向左走
return true;
} else { //说明这个点走不通
map[i][j] = 3;
return false;
}
} else { //如果map[i][j] != 0 ,可能是 1 2 3
//1 表示墙壁不能走
//3 表示此路不通
//2 表示已经探明可以走。不必再走
return false;
}
}
}
}