深度优先搜索(DFS)
类似于数的先根遍历,是数的先根遍历的推广。假设初始状态是图中所有的顶点都未曾被访问,则深度优先搜索可以从图中的某个顶点出发,访问此顶点,然后依次从v未被访问的邻接点出发深度优先遍历图,直至图中所有和v相通的顶点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为起始点,重复上诉过程,直到图中所有的顶点都被访问到为止。
广度优先搜索(BFS)
类似于树的按层次遍历的过程。假设从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问的邻接点,然后分别从这些邻接点出发依次访问他们的邻接点,并使 “先被访问的邻接点”先于“后被访问的邻接点” ,直到图中所有已被访问的顶点的邻接点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为起始点,重复上诉过程,直到图中所有的顶点都被访问到为止。换句话说,广度优先搜索遍历图的过程是以v为起始点,由近至远,依次访问和v有路径相通且路径长度为1,2,3…的顶点。
综上所诉,由于各自搜素的特性的区别。深度优先搜索(DFS)往往需要配合堆栈的使用,广度优先搜索(BFS)往往需要配合队列的使用.
迷宫问题
下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可以通行的地方。
010000
000100
001001
110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这个它的上、下、左、右四个方向之一。对于上面的迷宫,从入口开始,可以按DRRURRDDDR 的顺序通过迷宫,一共 10 步。其中 D、U、L、R 分别表示向下、向上、向左、向右走。
分析解答:
利用 深度优先搜索(DFS) 可以求出迷宫的所有路径中的一条路径,如果要求出迷宫的所有路径则还需要配合"回溯法"。
上图为DFS求解迷宫路径的过程,关键是要配合栈的使用,以及将走过的地方进行标记,防止在探索迷宫时进入死循环。
import java.util.Stack;
public class Draft {
public static void main(String[] args) {
// 创建一个方向数组,其优先级为"下右左上",这里要注意的是,x,y是行列坐标,和直角坐标系的移动是有差异的
int[][] direction = new int[][] { { 1, 0 }, { 0, -1 }, { -1, 0 }, { 0, 1 } };
// 迷宫的地图信息,我们在原有的地图的基础上在外围添加一圈墙壁,1 表示墙壁
int[][] graph = new int[][] { { 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 1, 0, 0, 1 }, { 1, 0, 0, 1, 0, 0, 1, 1 }, { 1, 1, 1, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1 } };
Stack<GPS> path = new Stack<>(); // 创建一个栈用来记录走过的路径
boolean flag = false; // 置标记flag判断迷宫是否有解
int x = 1, y = 1,D=-1; // 初始化起点的位置信息,将起点压入栈中
graph[x][y] = -1; // 标记该位置
GPS startGps = new GPS(x, y, D);
path.push(startGps);
loop: while (!path.isEmpty()) {
GPS tempGps = path.pop();
x = tempGps.x;
y = tempGps.y;
D = tempGps.d +1;
while(D<4){
int row = x + direction[D][0];
int col = y + direction[D][1];
if (graph[row][col] == 0) {
path.push(new GPS(x, y, D)); // 将当前的位置信息压入栈中
x = row; // 走到下一个节点,并将该结点标记
y = col;
graph[x][y] = -1;
if (x == 4 && y == 6) {
path.push(new GPS(4, 6, 0)); // 将终点压栈
flag = true; // 修改标记的值,表示迷宫有解
break loop; // 跳出标记所在的循环
} else {
D = 0;
}
} else {
D++;
}
}
}
if (flag) {
System.out.println("该迷宫有解");
Stack<GPS> realPath = new Stack<>(); // 将栈逆序输出即可得路径
while (!path.isEmpty()) {
realPath.push(path.pop());
}
while (!realPath.isEmpty()) {
GPS g = realPath.pop();
System.out.print("(" + g.x + "," + g.y + ")"+" ");
}
} else {
System.out.println("该迷宫无解");
}
}
}
// 表示当前所处的位置信息
class GPS {
int x; // 当前位置的行坐标
int y; // 当前位置的列坐标
int d; // 表示下一个可行位置的方向
// 全参数的构造方法
public GPS(int x, int y,int d) {
this.x = x;
this.y = y;
this.d = d;
}
}
利用 BFS(广度优先搜索) 求迷宫的一条最短路径,其关键在于配合队列的使用,以及对走过位置的标记。
如上图所示为广度优先搜索迷宫的过程,代码如下
import java.util.LinkedList;
/*
广度优先搜索解决,找出迷宫的最短路径
*/
public class Maze_BFS {
public static void main(String[] args) {
//创建一个方向数组,方向的优先级为 "下左上右"
Direction[] di = new Direction[] {new Direction(1,0),new Direction(0,-1),new Direction(-1,0),new Direction(0,1)};
//创建一个字符串数组,其中 D、U、L、R 分别表示向下、向上、向左、向右走。
StringBuffer[] step = new StringBuffer[] {new StringBuffer("D"),new StringBuffer("L"),new StringBuffer("U"),new StringBuffer("R")};
//创建队列,LinkedList为Queue<>接口的实现类
LinkedList<GPS> quene = new LinkedList<>();
//迷宫地图,起点为(1,1),终点为(4,6)
int[][] graph = new int[][] {{1,1,1,1,1,1,1,1},{1,0,1,0,0,0,0,1},{1,0,0,0,1,0,0,1},
{1,0,0,1,0,0,1,1},{1,1,1,0,0,0,0,1},{1,1,1,1,1,1,1,1}};
//创建一个标识符,判断迷宫是否有解
boolean b = false;
int x=1,y=1,stepNumber=0;
String startStep = "";
GPS temp = new GPS(x,y,stepNumber,startStep); //将起始点的信息加入队列
graph[x][y] = -1; //将当前位置标记为已经走过
quene.addLast(temp);
Loop:while(!quene.isEmpty()) {
temp = quene.poll() ; //弹出队头元素进行扩展
for(int i=0;i<4;i++) { //按照优先级"下左上右",依次进行扩展
int row = temp.x + di[i].inc_x;
int col = temp.y + di[i].inc_y;
StringBuffer ts = step[i]; //当前方向的字母表示
if(graph[row][col] == 0) {
int tempStepNumber = temp.stepNumber+1;
String tempStepPath = temp.stb + ts;
quene.addLast(new GPS(row,col,tempStepNumber,tempStepPath)); //符合条件的坐标加入队列
graph[row][col] = -1; //将该结点的值设为-1,扩展该结点
if(row==4 && col==6) { //判断是否到达了终点
b = true;
break Loop; //跳出标记所在的循环
}
}
}
}
if (b) {
System.out.println("该迷宫有解");
System.out.println("走出迷宫至少需要: " + quene.getLast().stepNumber + " 步"); //队尾元素存储的信息即是答案
System.out.println("最短路径的走法: " + quene.getLast().stb);
} else {
System.out.println("该迷宫无解");
}
}
}
/*
方向类
*/
class Direction{
int inc_x; //x方向的增量
int inc_y; //y方向的增量
public Direction(int inc_x,int inc_y) {
this.inc_x = inc_x;
this.inc_y = inc_y;
}
}
/*
GPS类,成员变量x,y表示坐标,stepNumber表示步数
*/
class GPS{
int x;
int y;
int stepNumber;
String stb; //用来记录路径
public GPS(int x,int y,int stepNumber,String stb){
this.x = x;
this.y = y;
this.stepNumber = stepNumber;
this.stb = stb;
}
}
以上即是对于BFS和DFS下的迷宫问题的概述。