递归(Recursion)
递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。
递归调用机制
- 当程序执行一个方法时,就会开辟一个独立的空间(栈)
- 每个空间的数据(局部变量),是独立的。
- 打印问题
/**
* 打印问题
* * @param n
*/
public static void test(int n) {
if (n > 2) {
test(n - 1);
}
System.out.println("n=" + n);
}
- 阶乘问题
/**
* 阶乘问题
*
* @param n
* @return
*/
public static int factorial(int n) {
if (n == 1) {
return 1;
} else {
return factorial(n - 1) * n;
}
}
递归需要遵守的重要规则
- 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
- 方法的局部变量是独立的,不会相互影响, 比如n变量
- 如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据.
- 递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError——死龟了:)
- 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。
迷宫问题
- map表示地图
- i,j表示从地图的哪个位置开始出发,如(1,1)
- 如果小球能到
map[6][5]
位置,则说明通路找到。 - 约定:当
map[i][j]
为0表示该点没有走过,当为1表示墙;2表示通路可以走;3表示该点已经走过,但是走不通 - 在走迷宫时,需要确定一个策略(方法)下->右->上->左,如果该点走不通,再回溯
迷宫地图
- 1:表示墙
- 0:表示可以走的通路
- 2:表示通路可以走
- 3:表示该点已经走过,但是走不通
代码实现
使用递归回溯来给小球找路
1. map表示地图
2. i,j表示从地图的哪个位置开始出发,如(1,1)
. 如果小球能到`map[6][5]`位置,则说明通路找到。
4. 约定:当`map[i][j]`为0表示该点没有走过,当为1表示墙;2表示通路可以走;3表示该点已经走过,但是走不通
. 在走迷宫时,需要确定一个策略(方法)下->右->上->左,如果该点走不通,再回溯
/**
* 使用递归回溯来给小球找路
* 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 如果找到则为真,否则为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)) {
//向下走
} 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,
return false;
}
}
//这句并不必要,只是不加IDE会报错
return false;
}
测试
/**
* Copyright (C), 2020-2020, 人生无限公司
* FileName: Maze
* Description: 模拟迷宫
*
* @create: 2020/9/7 15:08
* @author Reanon
* @version JDK 1.8.0_251
*/
package recursion;
public class Maze {
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 < map.length; 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;
//输出地图
System.out.println("地图的情况");
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[i].length; 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 < map.length; i++) {
for (int j = 0; j < map[i].length; 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 如果找到则为真,否则为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)) {
//向下走
} 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,
return false;
}
}
//这句并不必要,只是不加IDE会报错
return false;
}
/**
* 使用递归回溯来给小球找路
* 修改策略(方法)上->右->下->左
*
* @param map 表示地图
* @param i 从哪个位置开始找:行
* @param j 从哪个位置开始找:列
* @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)) {
//向上走
} 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,
return false;
}
}
//这句并不必要,只是不加IDE会报错
return false;
}
}
输出结果
//输出结果
地图的情况
1 1 1 1 1 1 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 1 1 0 0 0 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 1 1 1 1 1 1
输出小球走过,并标识过的地图
1 1 1 1 1 1 1
1 2 2 2 2 2 1
1 0 0 0 0 2 1
1 1 1 0 0 2 1
1 0 0 0 0 2 1
1 0 0 0 0 2 1
1 0 0 0 0 2 1
1 1 1 1 1 1 1