1、递归的概念
简单来说,递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂的问题,同时可以让代码变得更简洁。代价就是当递归次数过高时会占用过多的计算机资源。
2、递归要遵守的重要规则
1、执行一个方法时,就创建一个新的受保护的独立空间(栈空间);
2、方法的局部变量是独立的,不会相互影响;
3、如果使用的是引用类型变量,会共享该引用类型的数据;
4、递归必须向推出条件逼近,否则就无限递归,出现内存爆炸;
5、一个方法执行完毕或者遇到return,谁调用就把返回结果给谁。
3、递归的运用
3.1 迷宫问题
3.1.1 问题描述
假设有以下这样子的迷宫,由7×7的格子组成,红色各自表示墙壁,不能走,白色部分是可以走。假设(1,1)是起点,(6,6)是终点,编写程序把起点到终点走过的路线标出来。
3.1.2 代码实现
package cn.klb.datastructures.recursion;
/**
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @Description: 给定地图、起点和终点,找出路线
* @Date: Create in 2023/4/3 12:18
* @Modified By:
*/
public class Labyrinth {
private int[][] map; // 二维数组表示地图
/**
* 初始化地图
*
* @param map
*/
public Labyrinth(int[][] map) {
this.map = map;
// 左右设置墙
for (int i = 0; i < this.map.length; i++) {
this.map[i][0] = 1; // 左边墙设置为1
this.map[i][this.map.length - 1] = 1; // 右边墙设置为1
}
// 上下设置墙
for (int i = 0; i < this.map[0].length; i++) {
this.map[0][i] = 1;
this.map[this.map[0].length - 1][i] = 1;
}
// 设置障碍
this.map[3][1] = 1;
this.map[3][2] = 1;
}
/**
* 打印地图
*/
public void showMap() {
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[0].length; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
}
/**
* 根据起点找出走到以地图最右下角为终点的路线
* map[i][j] 默认值为 0 表示该点为未走过
* map[i][j] 设置为 2 表示该点为通路
* map[i][j] 设置为 3 表示该点走过,但是不是通路
* 走路逻辑:下 右 上 左
*
* @param i 起点横坐标
* @param j 起点纵坐标
* @return
*/
public boolean go1(int i, int j) {
// 如果终点标记为2,说明路线已经走完并且走成功了
if (map[map.length - 2][map.length - 2] == 2) {
return true;
} else {
// 如果终点标记不为2,说明还没走完
if (map[i][j] == 0) {
// 如果这个点未走过
map[i][j] = 2; // 假定这个点可以走,如果下面走不通再把它设为3
if (go1(i + 1, j)) {
// 如果往下方走一步是可以走的,说明当前位置是通路
return true;
} else if (go1(i, j + 1)) {
// 下方走不通,如果右边走的通,说明当前位置是通路
return true;
} else if (go1(i - 1, j)) {
// 右边也走不了,那就尝试走上边
return true;
} else if (go1(i, j - 1)) {
// 上边也走不了,那就尝试走左边
return true;
} else {
// 上下左右都不能走,说明当前位置是死路
map[i][j] = 3; // 打破了前面的假设,把当前点设置为3
return false;
}
} else {
// 如果这个点已经走过了,就不能再走了
return false;
}
}
}
/**
* 根据起点找出走到以地图最右下角为终点的路线
* map[i][j] 默认值为 0 表示该点为未走过
* map[i][j] 设置为 2 表示该点为通路
* map[i][j] 设置为 3 表示该点走过,但是不是通路
* 走路逻辑:右 上 左 下
*
* @param i 起点横坐标
* @param j 起点纵坐标
* @return
*/
public boolean go2(int i, int j) {
// 如果终点标记为2,说明路线已经走完并且走成功了
if (map[map.length - 2][map.length - 2] == 2) {
return true;
} else {
// 如果终点标记不为2,说明还没走完
if (map[i][j] == 0) {
// 如果这个点未走过
map[i][j] = 2; // 假定这个点可以走,如果下面走不通再把它设为3
if (go2(i, j + 1)) {
return true;
} else if (go2(i - 1, j)) {
return true;
} else if (go2(i, j - 1)) {
return true;
} else if (go2(i+1, j)) {
return true;
} else {
// 上下左右都不能走,说明当前位置是死路
map[i][j] = 3; // 打破了前面的假设,把当前点设置为3
return false;
}
} else {
// 如果这个点已经走过了,就不能再走了
return false;
}
}
}
}
3.3 八皇后问题
3.3.1 问题描述
八皇后问题是一个注明的问题,是递归算法的典型案例。再8×8的国际象棋上摆放八个皇后,任意两个皇后不能处于同一行、同一列或同一斜线上,问有多少种摆法。
3.3.2 算法思路
1、第一个皇后先放在第一行第一列;
2、第二个皇后放在第二行第一列,然后判断和上一个皇后是否冲突,不冲突则放下一个皇后,冲突则第二个皇后放到第二行第二列,再次判断,直到不冲突;
3、放置第三个皇后,还是放在第三行的第一列,然后判断和前面的皇后是否冲突,以此类推;
4、当得到一个正确的解时,在栈回退到上一个栈时,就会开始回溯,即第一个皇后放到第一列的所有正确解;
5、然后回头继续第一个皇后放到第一行第二列,依此循环上面步骤。
说明:理论上可以创建要给二维数组来表示棋盘,但是通过算法实际上可以用一个一维数组来解决问题,arr[8]={0,1,2,3,4,5,6,7},arr[i] = val表示第 i+1 个皇后放在第 i+1 行第 val+1 列。
3.3.3 代码实现
package cn.klb.datastructures.recursion;
/**
* @author DDKK.COM 弟弟快看,程序员编程资料站
* @Description:
* @Date: Create in 2023/4/3 14:06
* @Modified By:
*/
public class Queen8 {
private int max;
private int[] array; // 默认,第i个皇后放在第i行,数组保存的是该行的第几列
public int count = 0;
// 皇后的数量
public Queen8(int max) {
this.max = max;
array = new int[this.max];
this.check(0);
}
/**
* 放置第 n 个皇后
*/
private void check(int n) {
if (n == max) {
// n==max说明所有皇后都已经放好了
showQueen8();
return;
}
// 依此放入皇后并检查是否冲突
for (int i = 0; i < max; i++) {
array[n] = i; // 把第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] 表示判断第n个皇后是否和前面的n-1个皇后在同一列
// Math.abs(n-i) == Math.abs(array[n] - array[i]) 表示判断第n个皇后是否和第i皇后是否在同一斜线
if (array[i] == array[n] || Math.abs(n - i) == Math.abs(array[n] - array[i])) {
return false;
}
}
return true;
}
/**
* 显示摆放好的八皇后
*/
public void showQueen8() {
count++;
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
}
}