数据结构与算法——Java实现递归、迷宫回溯问题、八皇后问题

谁践踏了优雅 2024-03-30 09:21 107阅读 0赞

目录

一、递归

1.1 介绍递归

二、迷宫回溯问题

2.1 代码实现

三、八皇后问题

3.1 基本介绍

3.2 分析思路

3.3 代码实现


一、递归

1.1 介绍递归

简单的说:递归就是方法自己调用自己,每次传入不同的变量。

递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。

f1cedcb778d7481bb33b7d5a489738e5.png

递归可以解决什么样的问题?

  • 八皇后问题、汉诺塔、阶乘、迷宫、球和蓝子
  • 各种算法中也会使用到递归,比如快排,归并排序,二分查找,分治算法等
  • 把用栈解决的问题编程递归代码比较简洁

递归重要的规则

  • 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
  • 方法的局部变量是独立的,不会相互影响的
  • 如果方法中使用的是引用数据类型变量(比如数组,每一次递归都是用的同一个数据,修改的同一个数据),就会共享该引用数据类型的数据
  • 递归必须向退出递归的条件逼近,否则死循环
  • 当一个方法执行完毕,或者遇到return,就会返回。遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕。

二、迷宫回溯问题

最短路径和程序员找的策略有关

2.1 代码实现

  1. public class MiGong {
  2. public static void main(String[] args) {
  3. // 创建二维数组模拟迷宫 八行七列
  4. int[][] map = new int[8][7];
  5. // 数字1代表墙 第1行和第八行全是强
  6. for(int i=0;i<7;i++){
  7. map[0][i]=1;
  8. map[7][i]=1;
  9. }
  10. // 第一列和第七列全是墙
  11. for(int i=0;i<8;i++){
  12. map[i][0]=1;
  13. map[i][6]=1;
  14. }
  15. // 除此之外还有挡板
  16. map[3][1]=1;
  17. map[3][2]=1;
  18. // 模型输出一下
  19. for(int i=0 ;i<8 ; i++){
  20. for (int j=0; j<7;j++){
  21. System.out.print(map[i][j]+" ");
  22. }
  23. System.out.println();
  24. }
  25. System.out.println("***********************递归回溯开始!!!!!!*********************");
  26. // 递归回溯
  27. setWay(map,1,1);
  28. // 输出小球走过的路
  29. for(int i=0 ;i<8 ; i++){
  30. for (int j=0; j<7;j++){
  31. System.out.print(map[i][j]+" ");
  32. }
  33. System.out.println();
  34. }
  35. }
  36. /**
  37. * 递归开始处是11 结束处是65
  38. * 约定: map[i][j]=0时没有走过,1表示墙,2表示走过,是通路,3表示走过了这个路走不通
  39. * 如果能走到map[6][5]位置,既map[6][5],说明路能通
  40. * 确定策略:在走迷宫的时候按照 先走下面,走不通走右面,再走不通走上面,再走不通走左面 ,如果该点走不通再回溯
  41. *使用递归回溯给小球找路 找到为true
  42. * @param map 地图
  43. * @param i 从哪个位置开始找
  44. * @param j 从哪个位置开始找
  45. * @return 找到路返回true
  46. */
  47. public static boolean setWay(int[][] map,int i ,int j){
  48. if(map[6][5] ==2 ){
  49. // 到终点了
  50. return true;
  51. }else {
  52. // 没有到终点,继续走
  53. if(map[i][j] ==0){
  54. // 先假定能走通,我们走走试试
  55. map[i][j] =2;
  56. if( setWay(map, i+1, j) ){ //向下走
  57. return true;
  58. }else if(setWay(map, i, j+1)){ //向右
  59. return true;
  60. }else if(setWay(map, i-1, j)) { //向上
  61. return true;
  62. }else if(setWay(map, i, j-1)){ //向左
  63. return true;
  64. }else {
  65. // 运行到这里肯定是走不通
  66. map[i][j] =3;
  67. return false;
  68. }
  69. }else {
  70. // map[i][j] !=0 代表着可能是1,2,3,这三个都不满足我们走的条件
  71. return false;
  72. }
  73. }
  74. }
  75. }

a812eb41159a48d1af86ba1a111aa4bb.png

三、八皇后问题

3.1 基本介绍

八皇后问题:是回溯算法的基本案例。在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后不能处于同一行、同一列或同一斜线,问有几种摆法

答案是92种

3.2 分析思路

首先说明:回溯的效率并不高,因为这就好像是每个路都走走试试行不行,这种方法就类似我们上学时学的穷举法,一个一个的试。比如我们这个8*8的棋盘就要执行一万五千多次,可见效率情况

237e522a96134859a88c6ecc83d4b489.png

解题说明:理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法简化。用一个一维数组即可解决问题,arr[8]={0,4,7,5,2,6,1,3},其中数组下标对应第几列,数组下标对应的数值对应皇后放在哪个位置。比如arr[i]=var,var表示第i+1个皇后放在第i+1行的第val+1列

3.3 代码实现

  1. public class Queue8 {
  2. // 定义一个max,表示共有多少个皇后
  3. int max =8; //8*8的棋盘,有8个皇后
  4. int[] array = new int[max]; //定义数组,存放皇后存在的位置
  5. int count =0;
  6. public static void main(String[] args) {
  7. // 测试
  8. Queue8 queue8 = new Queue8();
  9. queue8.check(0);
  10. }
  11. // 编写放置皇后的方法(n从0开始计数)
  12. // check每一次递归时进入到check都会有for循环,可以理解为多层for循环,这个想法太好了
  13. private void check(int n){
  14. if(n == max){
  15. count++;
  16. System.out.println(count);
  17. print(); //我们自己编写的,输出
  18. // //n=8,表示对数组来说就是第九个,很显然已经超出范围了,故我们的棋盘已经找出结果了
  19. return;
  20. }
  21. // 依次放入皇后,并判断是否冲突
  22. for(int i=0;i<max;i++){
  23. // 放置当前皇后n,放到改行的第一列
  24. // 第n行第i列
  25. array[n] =i;
  26. // 我们应该判断一下,第n行第i列放了行不行
  27. if (judge(n)){
  28. // 运行到这里说明不冲突,然后再放下一个
  29. check(n+1); //for循环结束或者n==max之后,就会产生回溯
  30. }
  31. // 上面是不冲突的,万一冲突怎么办?
  32. // 就会i++,又放到此行的下一个位置再次重试
  33. }
  34. }
  35. /**
  36. * 查看当我们放置第n个皇后,就去检测该皇后是否与前面已经摆放的皇后冲突
  37. * 这个地方我们不需要判断是否在同一行,因为我们使用了数组,数组下标代表行,故每行只能放一个
  38. * n从零开始计数
  39. * @param n 表示第几个皇后
  40. * @return
  41. */
  42. private boolean judge(int n){
  43. for(int i=0;i<n;i++){
  44. if(array[i]==array[n] ||Math.abs(n-i)==Math.abs(array[n]-array[i])){
  45. // array[i]==array[n] 表示在同一列
  46. // Math.abs(n-i)==Math.abs(array[n]-array[i])表示判断第n个皇后是否和第i皇后在同一个斜线上
  47. // 其实最后一个很好理解 把上面的式子变形,我们带入一下初中学的平面直角坐标系,
  48. // (n-i)/(array[n]-array[i]) = -1 或1 通俗的来说就行y的变化量比上x的变化量等于正一或负一
  49. // 这样就是在同一个斜线上,这个地方真的有点巧妙
  50. return false;
  51. }
  52. }
  53. // 返回true说明冲突
  54. return true;
  55. }
  56. // 写一个方法,可以将皇后摆放的位置输出
  57. private void print(){
  58. for(int i=0; i<array.length;i++){
  59. System.out.print(array[i]+"");
  60. }
  61. System.out.println();
  62. }
  63. }

95dce6e402304a579e3eb68d9e98f392.png

我们可以仔细的看一下下面这个图,我们看下标为0的数,是从0开始逐渐增大的,说明我们在分析阶段分析的没毛病。先把皇后在第一行的所有情况都找到,然后再把第二行的所有情况都找到,就这样依次执行到第八行,最终完成

3f134482c708458b8dd432c46040bca8.png

发表评论

表情:
评论列表 (有 0 条评论,107人围观)

还没有评论,来说两句吧...

相关阅读