本文章用一个简单的问题引入
问题描述:
下面表示迷宫(5*4)的地图,1表示为可通过,2表示为障碍,不能通过,
起点是(1,1),终点是(4,3),按优先级为:右 ,下, 左, 上 的顺序走,
请问最短路径是多少?
1 1 2 1
1 1 1 1
1 1 2 1
1 2 1 1
1 1 1 2
升华版无非就是地图大了,或者要打印路径,都只需在此基础稍微添加一些代码即可,当然当规模变大时,此代码还需优化,需添加一些条件来去掉一些答案(剪枝),否则提交要么超时,要么一下运行不出来。此代码先不作太多优化,用来学习bfs和dfs的初学者学习。
好了,废话不多说,上代码:
1.DFS(深度优先搜索)
基本模型
void dfs(int step){
判断边界
尝试每一种可能 for(i=1;i<=n;i++){
继续下一步 dfs(step+1)
}
返回
}
具体怎么搜索动画可参考下面的动图:
dfs(一)代码如下
import java.util.Arrays;
import java.util.Scanner;
public class Dfs_MIGong {
static int m = 5, n = 4;//行 列数
static int min = 9999;//最短步数
static int p = 4, q = 3;//终点坐标
static int[][] map = new int[100][100];//存储地图
static boolean[][] vis = new boolean[100][100];//标记是否走过
void dfs(int x, int y, int step) {
//1.截止条件
if (x == p && y == q) {//当到达终点时
min = Math.min(min, step);
return;//注意这里的return不是回到主函数,而是结束当前的递归回到上一个dfs
}
//当越界或者碰到障碍时,返回
if (x > m || y > n || map[x][y] == 2) {
return;//同上
}
//2.候选结点
//右
if (map[x][y + 1] == 1 && !vis[x][y + 1]) {
vis[x][y + 1] = true;
dfs(x, y + 1, step + 1);
//回溯
vis[x][y + 1] = false;
}
//下
if (map[x + 1][y] == 1 && !vis[x + 1][y]) {
vis[x + 1][y] = true;
dfs(x + 1, y, step + 1);
//回溯
vis[x + 1][y] = false;
}
//左
if (map[x][y - 1] == 1 && !vis[x][y - 1]) {
vis[x][y - 1] = true;
dfs(x, y - 1, step + 1);
//回溯
vis[x][y - 1] = false;
}
//上
if (map[x - 1][y] == 1 && !vis[x - 1][y]) {
vis[x - 1][y] = true;
dfs(x - 1, y, step + 1);
//回溯
vis[x - 1][y] = false;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
map[i][j] = sc.nextInt();
}
}
int startx = 1;//起点x的坐标
int starty = 1;//起点y的坐标
Dfs_MIGong t = new Dfs_MIGong();
// 初始化
vis[startx][starty] = true;
t.dfs(startx, starty, 0);
System.out.println(min);
}
}
细心的同学可能发现,该代码搜索的时候相似的很多,因此可以稍微优化一下,添加方向数组来优化
//按照 顺时针,右 下 左 上 顺序走
static int[] dx = new int[]{0, 1, 0, -1};
static int[] dy = new int[]{1, 0, -1, 0};
//2.候选结点
for (int i = 0; i < 4; i++) {
int tx = x + dx[i];
int ty = y + dy[i];
if (map[tx][ty] == 1 && !vis[tx][ty]) {
vis[tx][ty] = true;
dfs(tx, ty, step + 1);
vis[tx][ty] = false;//回溯
}
}
2.BFS(广度优先搜索)
向四周扩散似的搜索
import java.util.LinkedList;
import java.util.Scanner;
public class Bfs_MiGong {
static int[][] map = new int[100][100];//地图
static boolean[][] vis = new boolean[100][100];//存储是否走过
static int m = 5, n = 4;//行 列数
static int endX = 4, endY = 3;//终点坐标
//按照 顺时针,右 下 左 上 顺序走
static int[] dx = new int[]{0, 1, 0, -1};
static int[] dy = new int[]{1, 0, -1, 0};
//用来记录点(x,y)
static class Node {
int x;//x坐标
int y;//y坐标
int step;//步数
public Node() {
}
public Node(int x, int y, int step) {
this.x = x;
this.y = y;
this.step = step;
}
}
static void bfs(int x, int y, int step) {
LinkedList<Node> q = new LinkedList<>();//LinkedList既可以看做是一个队列又可以看做一个栈,非常好用
q.addFirst(new Node(x, y, step));//加入该点
vis[x][y] = true;//标记已访问
while (q.size() != 0) {
Node temp = q.getFirst();//获取头结点
if (temp.x == endX && temp.y == endY ) {
//到终点了
System.out.println(temp.step);//bfs的路径一定是最短的,所以不需要判断
return;
}
for (int i = 0; i < 4; i++) {
int tx = temp.x + dx[i];
int ty = temp.y + dy[i];
if (tx > 0 && tx <= 5 && ty > 0 && ty <= 4 && !vis[tx][ty] && map[tx][ty] == 1) {
vis[tx][ty] = true;
q.addLast(new Node(tx, ty, temp.step + 1));
}
}
q.removeFirst();
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
map[i][j] = sc.nextInt();
}
}
int startX = 1;
int startY = 1;
vis[startX][startY] = true;
bfs(startX, startY, 0);
}
}
0 | 1 | -99 | 5 |
1 | 2 | 3 | 4 |
2 | 3 | -99 | 5 |
3 | -99 | 7 | 6 |
4 | 5 | 6 | -99 |
上图是bfs的搜索情况,-99表示是障碍用红色标记,绿色表示终点
每个方格的数字表示从起点走到该点的的步数(向四周扩散)
如果实现步骤还看不懂的推荐去b站看老师演示,每步都有具体的演示,再结合自己断点调试,我相信可以看懂的。
最后推荐一下LeetCode上的题目:不同路径 可以拿来练练手