1. 递归的概念
1.1 文字描述
简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量。
递归有助于编程者解决复杂的问题,同时可以让代码变得简洁
。
1.2 图示
就以阶乘举例的图示
2. 递归的应用场景
2.1 递归能解决什么样的问题
2.1.1 各种数学问题
8皇后问题 , 汉诺塔, 阶乘问题, 迷宫问题, 球和篮子的问题(google编程大赛)
2.1.2 各种算法
快排,归并排序,二分查找,分治算法等。
2.1.3 解决栈的问
递归归代码比较简洁。【树,图,最小生成树】
2.2 递归需要遵守的重要规则
- 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
- 方法的局部变量是独立的,不会相互影响, 比如num变量【num:图示里的变量】
- 如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据.【引用类型变量会都放到
堆内存
里】 - 递归必须向退出递归的条件逼近,否则就是无限递归,出现【
栈溢出
】StackOverflowError,死循环) - 当一个方法执行完毕,或者遇到return,就会返回,
遵守谁调用,就将结果返回给谁
,同时当方法执行完毕或者返回时,该方法也就执行完毕。
3.迷宫回溯
3.1思路分析【图示】
3.2 代码实现
package recursion;
import java.util.Random;
/**
* @Author 小源同学
* @Date 2022 02 17 01 59
* @Describe 迷宫回溯
**/
public class MazeBacktracking {
public static void main(String[] args) {
int y = 8;
int x = 7;
int[][] ints = initMap(y, x);
System.out.println("初始化迷宫:");
soutInt(ints);
System.out.println("迷宫回溯后的路径:");
findMapPath(ints, 1, 1, x, y);
soutInt(ints);
}
/**
* 迷宫回溯的核心代码
* 1. 设置终点为map[y-2][x-2]
* 2. 使用0:表示未走过;使用1:表示墙体;使用2:表示通路;使用3:表示不可通行
* 3. 寻路策略:下->右->上->左,如果路不通,回溯
*
* @param map 初始化的地图
* @param i 开始的横坐标比如:map[1][1]
* @param j 开始的纵坐标比如:map[1][1]
* @param x 地图的列数
* @param y 地图的行数
* @return 找到通路返回true, 如果没有找到就返回false
*/
private static boolean findMapPath(int[][] map, int i, int j, int x, int y) {
if (map[y - 2][x - 2] == 2) {//表示已经找到通路,直接返回,如果再回溯,就退出回溯
return true;
} else {
if (map[i][j] == 0) {//如果该点没有走过
//假设这个点可以走
map[i][j] = 2;
//然后再判断这个点是否可以通向其他点
if (findMapPath(map, i + 1, j, x, y)) {//【根据策略】向下走
return true;
} else if (findMapPath(map, i, j + 1, x, y)) {//【根据策略】向右走
return true;
} else if (findMapPath(map, i - 1, j, x, y)) {//【根据策略】向上走
return true;
} else if (findMapPath(map, i, j - 1, x, y)) {//【根据策略】向左走
return true;
} else {//证明这个点不可以走
map[i][j] = 3;
return false;
}
} else {//ap[i][j] != 0 那么这个点的值可能为 1 2 3,则不再继续
return false;
}
}
}
/**
* 初始化迷宫地图,用二维数组(int[8][7])表示,根据需求创建一个地图
*
* @param x 列
* @param y 行
* @return 返回初始化的地图
*/
private static int[][] initMap(int y, int x) {
//使用1:表示墙体
//使用2:表示通路
//使用3:表示不可通行
int[][] map = new int[y][x];
//初始化上下的墙体:遍历列【x】
for (int i = 0; i < x; i++) {
map[0][i] = 1;
map[y - 1][i] = 1;
}
//初始化左右墙体:遍历行【y】
for (int j = 0; j < y; j++) {
map[j][0] = 1;
map[j][x - 1] = 1;
}
//随机创建阻挡
Random rn = new Random();
//2到y-1之间的数字
int randomY = rn.nextInt(y - 2) + 2;
//添加到迷宫里
//1到3之间的数字
//如果要修改的话,根据设定的迷宫行与列进行修改
map[rn.nextInt(y - 2) + 2][rn.nextInt(x-2) + 1] = 1;
map[rn.nextInt(y - 2) + 2][rn.nextInt(x-2) + 1] = 1;
return map;
}
//打印输出 int[][]
private static void soutInt(int[][] ints) {
for (int[] anInt : ints) {
for (int i : anInt) {
System.out.print(i + " ");
}
System.out.println();
}
}
}
4.八皇后
4.1 问题介绍
八皇后问题
,是一个古老而著名的问题,是回溯算法的典型案例。
该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:
在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上
,问有多少种摆法。
4.2 思路分析【图示】
4.3 代码实现
package recursion;
/**
* @Author 小源同学
* @Date 2022 02 17 22 17
* @Describe 八皇后问题的解决
**/
public class Queue8 {
static int count = 0;//统计解决方法的数量
int max = 8;//定义棋盘的大小【行列一样】
int[] array = new int[max]; //保存皇后被放置的位置
public static void main(String[] args) {
Queue8 queue8 = new Queue8();
queue8.check(0);//从0开始,实际上是从第一行第一列开始
System.out.printf("总共有%d中解法", count);
}
//下棋:放置第n[0,1,……]个皇后
private void check(int n) {
if (n == max) {//结束递归【回溯】,max个皇后已经放置完毕
print();//打印结果
return;
}
//没有到我们设置的最大阈值,则继续递归【回溯】:依次放入皇后
for (int i = 0; i < max; i++) {
//从第一列开始放置,查找可行的位置
array[n] = i;
if (judge(n)) {//判断第n个皇后放在第i的位置是否会冲突【合不合理】
//不冲突的情况下,继续放置下一个皇后【开始递归】
check(n + 1);
}
//如果冲突就继续执行array[n] = i;就是改边列,依次尝试
}
}
//判断皇后摆放的是否合理
//当我们摆放了第n个皇后,判断该皇后和前面的(n-1)个皇后的位置是否冲突【任意两个皇后都不能处于同一行、同一列或同一斜线上】
private boolean judge(int n) {
for (int i = 0; i < n; i++) {
/*
* 说明:
* (下标):(下标+1)说明是第几个皇后,同时也是第几行
* (array[下标]):说明某行皇后在哪一列上
* 1. array[n] == array[i] :判断第n个皇后 与 前(n-1)个皇后是否在同一列
* 1.1 array[n] 代表的是第n个皇后所在的列,array[i] 代表的是前(n-1)个皇后所在的列 如果相等就说明在同一列上
* 2. Math.abs(n - i) == Math.abs(array[n] - array[i]) 这个比较不好理解,这个是为了判断是否在对角线上【斜线上】
* 2.1 假设 n=1,这表示是第2个皇后,前面只有一个,所以i = 0
* 2.2 Math.abs(n - i) = 1
* 2.3 Math.abs(array[1] - array[0]) 表示的列数相减 ,即 1-0 = 1 【这个时候画个图就会明白,这里考虑的是不是等腰直角三角形的两个腰】
*/
if (array[n] == array[i] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
return false;
}
}
return true;
}
//打印皇后摆放的位置输出
private void print() {
count++;
for (int i : array) {
System.out.print(array[i] + " ");
}
System.out.println();
}
}