1.递归应用场景
看个实际应用场景,迷宫问题(回溯), 递归(Recursion)
2.递归的概念
简单的说:递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂的问题,同时
可以让代码变得简洁。
3.递归调用机制
我列举两个小案例,来帮助大家理解递归,部分学员已经学习过递归了,这里在给大家回顾一下递归调用机制
1)打印问题
2)阶乘问题
3)使用图解方式说明了递归的调用机制
4)代码
public class RecursionTest {
public static void main(String[] args) {
test(5);
System.err.println(factorial(6));
}
public static void test(int n) {
if (n > 2) {
test(n - 1);
} //else {
System.out.println("n=" + n);
// }
}
//阶乘问题
public static int factorial(int n) {
if (n == 1) {
return 1;
} else {
return factorial(n - 1) * n; // 1 * 2 * 3
}
}
}
4.递归能解决什么样的问题
1)各种数学问题如: 8皇后问题,汉诺塔,阶乘问题,迷宫问题,球和篮子的问题(google编程大赛)
2)各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等.
3)将用栈解决的问题—>第归代码比较简
5.递归需要遵守的重要规则
1)执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
2)方法的局部变量是独立的,不会相互影响,比如n变量
3)如果方法中使用的是引用类型变量(比如数组),就会共享该引用类型的数据.
4)递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError,死龟了:)
5)当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕
6.迷宫
思路
- 使用递归回溯来给小球找路
- map表示地图
- i,j表示从地图的哪个位置开始出发(1,1)
- 如果小球能到map[6][5]位置,则说明通路找到.
- 约定: 当map[i][j]为0表示该点没有走过 当为1表示墙;2表示通路可以走 ;3表示该点已经走过,但是走不通尚在走迷宫时,需要确定一个策略(方法)下->右->上->左,如果该点走不通,再回溯
代码
```java package com.sgg.datastructure.recursion;
public class 迷宫 { /**
* @param i 行
* @param j 列
*/
public static int[][] init(int i, int j) {
int[][] map = new int[i][j];
//外面一圈是墙
for (int x = 0; x < 7; x++) {
map[0][x] = 1;
map[7][x] = 1;
}
for (int y = 1; y < 7; y++) {
map[y][0] = 1;
map[y][6] = 1;
}
return map;
}
public static void show(int[][] map) {
for (int[] ints : map) {
for (int i : ints) {
System.out.printf("%d\t", i);
}
System.out.println();
}
}
public static void main(String[] args) {
int[][] map = init(8, 7);
map[3][1] = 1;
map[3][2] = 1;
show(map);
System.out.println(setWay(map,1,1,6,5));
show(map);
}
/**
* 利用递归解决迷宫问题
* 思路,0代表没走过,1代表墙,2代表走成功了,3代表不成功
* 先下,然后右,然后走上,然后左
* @param map
* @param iy
* @param ix
* @param resultY
* @param resultX
* @return
*/
public static boolean setWay(int[][] map, int iy, int ix, int resultY, int resultX) {
if (map[resultY][resultX] == 2) {
return true;
}
if (map[iy][ix]==0) {
map[iy][ix] = 2;
if (setWay(map, iy+1, ix ,resultY,resultX)) {//下
return true;
}else if(setWay(map, iy, ix+1 ,resultY,resultX)){//右
return true;
} else if (setWay(map, iy-1, ix, resultY, resultX)) {//上
return true;
}else if (setWay(map, iy, ix-1, resultY, resultX)) {//左
return true;
}else {
map[iy][ix] = 3;
return false;
}
}else {
return false;
}
}
}
<a name="CNqlg"></a>
## 作业
求最短的路径
<a name="pSkdw"></a>
### 思路
用不同的策略,然后比较需要走的步数
<a name="LjpIU"></a>
### 代码
以后做<br />![03.jpeg](https://cdn.nlark.com/yuque/0/2021/jpeg/655064/1640055017527-f5bfb798-1f6c-44a4-ae25-eae6f55cff3c.jpeg#clientId=u902c52c0-b80c-4&crop=0&crop=0&crop=1&crop=1&from=ui&id=u5f9da45f&margin=%5Bobject%20Object%5D&name=03.jpeg&originHeight=150&originWidth=150&originalType=binary&ratio=1&rotation=0&showTitle=false&size=8284&status=done&style=none&taskId=u7f21b5e3-2ea2-4cce-a414-5a795106d93&title=)
<a name="c8HnQ"></a>
# 7.八皇后
<a name="mWtuR"></a>
## 介绍
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法(92)
<a name="Ugzow"></a>
## 思路
1. 第一个皇后先放第一行第一列
1. 第二个皇后放在第二行第一列、然后判断是否OK, 如果不OK,继续放在第二列、第三列、依次把所有列都<br />放完,找到一个合适
1. 继续第三个皇后,还是第一列、第二列......直到第8个皇后也能放在一个不冲突的位置,算是找到了一个正确<br />解
1. 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正确解,<br />全部得到.
1. 然后回头继续第一个皇后放第二列,后面继续循环执行1,2,3,4的步骤
1. 示意图:
![image.png](https://cdn.nlark.com/yuque/0/2021/png/655064/1640055178385-dd47c8e6-e321-4fa3-9404-c5f80b4f7bcf.png#clientId=u902c52c0-b80c-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=301&id=u14ecbf80&margin=%5Bobject%20Object%5D&name=image.png&originHeight=401&originWidth=664&originalType=binary&ratio=1&rotation=0&showTitle=false&size=236418&status=done&style=none&taskId=ubd1eaa08-e0be-4a23-adb0-535f79a4f29&title=&width=498)
<a name="zs0ME"></a>
## 说明
理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法,<br />用一个**一维数组**即可解决问题. arr[8] ={0 , 4, 7, 5, 2, 6, 1, 3} <br />对应arr下标 表示第几行,即第几个皇后,arr[i] = val , val表示第i+1个皇后,放在第i+1行的第val+1列
<a name="FWH4h"></a>
## 代码
```java
package com.sgg.datastructure.recursion;
import java.util.Arrays;
public class Queue8 {
int max = 8;//代表几个皇后,也代表棋盘的大小
int[] array = new int[max];//保存皇后放置位置的结果,比如 arr = {0 , 4, 7, 5, 2, 6, 1, 3}
static int count = 0;//有几种可能
/**
* 放置第n个皇后
*/
public void check(int n) {
if (n == max) {//跳出的条件
print();
return;
}
for (int i = 0; i < max; i++) {
array[n] = i;
if (judge(n)) {
check(n + 1);
}
}
}
/**
* 判断放置了这个皇后,跟之前的是否冲突
*/
private boolean judge(int n) {
for (int i = 0; i < n; i++) {
if (array[i] == array[n]) {
return false;
}
if (Math.abs(n - i) == Math.abs(array[n] - array[i])) {
return false;
}
}
return true;
}
private void print() {
count++;
Arrays.stream(array).forEach(x -> System.out.print(x + " "));
System.out.println();
}
public static void main(String[] args) {
Queue8 queue8 = new Queue8();
queue8.check(0);
System.out.printf("一共有%d 解法", count);
}
}