问题的解空间通常是在搜索问题的解的过程中动态产生的,这是回溯算法的一个重要特性。
2、问题定义
问题:给定一个迷宫,找到从入口到出口的所有可行路径。如下图所示:
思路:
由于路径不能重复进入矩阵的格子,还需要定义和字符矩阵大小一样的布尔值矩阵,用来标识路径是否已经进入每个格子。 当矩阵中坐标为(row,col)的格子和路径字符串中相应的字符一样时,从4个相邻的格子(row,col-1),(row-1,col),(row,col+1)以及(row+1,col)中去定位路径字符串中下一个字符如果4个相邻的格子都走不通时,表明当前路径不正确,我们需要回到前一个,然后重新规划路线。一直重复这个过程,直到找到终点。
解题步骤:
1)、从起点开始,逐步向其它路径四周扩展路径。
2)、每走一步判断,如果未到达继续四周扩展路径。
3)、记录下路径,如果走到思路则回退到上一步。
先贴上第一个版本的代码,次代码只能返回是否有路径,比较简单一点:
package com.qian.backtracing;
public class Maze {
public boolean hasPath(boolean[][] road, int sRow, int sCol, int tRow, int tCol) {
boolean[][] hasVisited = new boolean[road.length][road[0].length];
if(!road[sRow][sCol] || !road[tRow][tCol]) return false;
for (int i = 0; i < road.length; i++) {
for (int j = 0; j < road[0].length; j++) {
if(road[i][j])
System.out.print("√,");
else
System.out.print("x,");
}
System.out.println();
}
boolean rst = hasPathCore(road, sRow, sCol, tRow, tCol, hasVisited);
return rst;
}
/**
* 回溯递归函数
* @param road 路线图
* @param row 行号
* @param col 列号
* @param tRow 终点行号
* @param tCol 终点列号
* @param hasVisited 是否被访问数组
* @return
*/
public boolean hasPathCore(boolean[][] road, int row, int col, int tRow, int tCol,boolean[][] hasVisited) {
int cols = road[0].length;
int rows = road.length;
//System.out.println(cols + ";;" + rows );
if (col < 0|| col >= cols
|| row < 0|| row >= rows
|| !road[row][col] //路是通的
|| hasVisited[row][col])
return false;
if (row == tRow && col == tCol) //递归函数的出口
return true;
hasVisited[row][col] = true;
if(hasPathCore(road, row - 1, col, tRow, tCol, hasVisited)
|| hasPathCore(road, row, col - 1, tRow, tCol, hasVisited)
|| hasPathCore(road, row + 1, col, tRow, tCol, hasVisited)
|| hasPathCore(road, row, col + 1, tRow, tCol, hasVisited)) {
return true;
}
hasVisited[row][col] = false;
return false;
}
public static void main(String[] args) {
boolean[][] road = {{true,true,true},{false,true,false},{false,true,true}};
boolean[][] road2 = {{true ,true ,true ,false,false},
{false,false,true ,false,false},
{false,true ,true ,false,false},
{false,true ,false,false,false},
{false,true ,true ,true ,true }};
Maze maze = new Maze();
System.out.println(maze.hasPath(road2, 0, 0, 4, 4));
}
}
再贴上完整版的代码,能找到所有路径并输出:
package com.qian.backtracing;
import java.util.ArrayList;
public class Maze2 {
private int sum = 0;
private ArrayList> paths = new ArrayList<>();
private ArrayList path = new ArrayList();
public int hasPath(boolean[][] road, int sRow, int sCol, int tRow, int tCol) {
boolean[][] hasVisited = new boolean[road.length][road[0].length];
if (!road[sRow][sCol] || !road[tRow][tCol])
return 0;
hasPathCore(road, sRow, sCol, tRow, tCol, hasVisited,0);
return sum;
}
/**
* 回溯递归函数
* @param road 路线图
* @param row 行号
* @param col 列号
* @param tRow 终点行号
* @param tCol 终点列号
* @param hasVisited 是否被访问数组
* @param depth 访问深度 用于路径的添加
*/
public void hasPathCore(boolean[][] road, int row, int col, int tRow,int tCol, boolean[][] hasVisited,int depth) {
int cols = road[0].length;
int rows = road.length;
if (col < 0 || col >= cols || row < 0 || row >= rows || !road[row][col] // 路是通的
|| hasVisited[row][col]) {
return;
}
path.add("(" + row + "," + col + ")");
if (row == tRow && col == tCol) {// 递归函数的出口
sum++;
paths.add(new ArrayList(path));
path.remove(depth);
return;
} else {
hasVisited[row][col] = true;
hasPathCore(road, row - 1, col, tRow, tCol, hasVisited,depth + 1);
hasPathCore(road, row, col - 1, tRow, tCol, hasVisited,depth + 1);
hasPathCore(road, row + 1, col, tRow, tCol, hasVisited,depth + 1);
hasPathCore(road, row, col + 1, tRow, tCol, hasVisited,depth + 1);
// return true;
path.remove(depth);
hasVisited[row][col] = false;
// return false;
}
}
public static void main(String[] args) {
boolean[][] road = { { true, true, true }, { false, true, false },
{ false, true, true } };
boolean[][] road2 = {
{ true , true , true , true , true },
{ false, false, true , false, true },
{ false, true , true , true , true },
{ false, true , false, false, true },
{ false, true , true , true , true } };
//打印路径
System.out.println("地图为:");
for (int i = 0; i < road2.length; i++) {
for (int j = 0; j < road2[0].length; j++) {
if (road2[i][j])
System.out.print("√,");
else
System.out.print("x,");
}
System.out.println();
}
Maze2 maze = new Maze2();
System.out.println("一共有" + maze.hasPath(road2, 0, 0, 4, 4) + "条路径");
//打印路径
System.out.println("分别为:");
for (ArrayList p : maze.paths) {
System.out.println(p);
}
}
}
输出结果为:
地图为:
√,√,√,√,√,
x,x,√,x,√,
x,√,√,√,√,
x,√,x,x,√,
x,√,√,√,√,
一共有4条路径
分别为:
[(0,0), (0,1), (0,2), (1,2), (2,2), (2,1), (3,1), (4,1), (4,2), (4,3), (4,4)]
[(0,0), (0,1), (0,2), (1,2), (2,2), (2,3), (2,4), (3,4), (4,4)]
[(0,0), (0,1), (0,2), (0,3), (0,4), (1,4), (2,4), (2,3), (2,2), (2,1), (3,1), (4,1), (4,2), (4,3), (4,4)]
[(0,0), (0,1), (0,2), (0,3), (0,4), (1,4), (2,4), (3,4), (4,4)]