简介
简单的讲:递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂的问题,同时让代码变得简洁。
递归可以解决的问题
- 各种数学问题: 8皇后问题,汉诺塔,阶乘问题,迷宫问题,球和篮子的问题
- 各种算法中也会使用到,比如:快排,归并排序,二分查找,分治算法等
- 将用栈解决的问题,改为使用递归,使代码更简洁
递归需要遵守的规则
- 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
- 方法的局部变量是独立的,不会相互影响
- 如果方法中使用的是引用类型变量,就会共享该引用类型的数据
- 递归必须向退出递归的条件逼近,否则就是无限递归
- 当一个方法返回完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕
迷宫问题
package com.atguigu.recursion;
public class MiGong {
public static void main(String[] args) {
//创建一个二维数组来模拟迷宫
int[][] miGong = new int[8][7];
//使用1表示墙
for (int i = 0; i < 8; i++) {
miGong[i][0] = 1;
miGong[i][6] = 1;
}
for (int i = 0; i < 7; i++) {
miGong[0][i] = 1;
miGong[7][i] = 1;
}
miGong[3][1] = 1;
miGong[3][2] = 1;
System.out.println("------输出地图情况-------");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(miGong[i][j] + " ");
}
System.out.println();
}
//使用递归回溯找路
setWay(miGong, 1, 1);
System.out.println("------输出路径情况-------");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(miGong[i][j] + " ");
}
System.out.println();
}
}
/**
* 使用递归回溯找路径
* 说明:
* 1.如果小球可以到 map[6][5] ,则说明找到通路
* 2.约定:当 map[i][j] 为0时,表示该点没有走过,
* 当为1时,表示墙,
* 为2表示通路可以走,
* 为3表示该位置已经走过,但是不通
* 3.需要确定一个策略(方法),下-->右-->上-->左,如果该点走不通,再回溯
* @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
return false;
}
}
}
}
八皇后问题
思路分析
- 第一个皇后放在第一行第一列
- 第二个皇后放在第二行第一列、判断是否可行,如果不可行,继续放在第二列、第三列、依次把所有列都放完,找到一个合适的位置
- 继续第三个皇后,还是第一列、第二列…直到8个皇后都找到合适的位置,此时算是找到一个正确解
- 当得到一个正确解之后,栈回退到上一个栈,开始回溯,即得到第一个皇后在第一列上的正确解得到
- 然后将第一个皇后放在第一行第二列、第三列…循环1,2,3,4步,直到获得所有解
package com.atguigu.recursion;
/**
* 8皇后问题解法
*/
public class Queue8 {
/**
* 8个皇后
*/
int max = 8;
/**
* 存放8皇后的位置,下标表示行,值表示列
*/
int[] array = new int[max];
/**
* 记录一共有多少种解法
*/
static int count = 0;
/**
* 记录一共比较了多少次
*/
static int judgeCount = 0;
public static void main(String[] args) {
Queue8 queue8 = new Queue8();
queue8.check(0);
System.out.printf("一共有 %d 种解法", count);
System.out.println();
System.out.printf("一共进行了 %d 次比较", judgeCount);
}
private void check(int n) {
//n==max 时对应的是第9个皇后,说明已经找到了一种解法,打印
if (n == max) {
count++;
print(array);
return;
}
//寻找皇后位置
for (int i = 0; i < max; i++) {
//先假设当前的位置就是合适的位置
array[n] = i;
//看看是否有冲突
if (judge(n)) {
//没有冲突就走下一个皇后,开始递归
check(n + 1);
}
//如果找不到,就进行回溯,通过改变上一个皇后的位置,重新进行递归,直到找到所有的解法
}
}
/**
* 判断第n个皇后是否和前几个皇后冲突
* @param n
* @return
*/
private boolean judge(int n) {
for (int i = 0; i < n; i++) {
//array[i] == array[n] 相等说明在同一列必定会冲突
//Math.abs(n - i) == Math.abs(array[n] - array[i]) 相等说明在同一条斜线上
judgeCount++;
if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
return false;
}
}
return true;
}
/**
* 打印解法
* @param array
*/
private void print(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
}
System.out.println();
}
}