# BFS广度优先遍历寻找最短路径(超详细实现过程)

最近一直想搞A*算法，发现有部分没理解清楚。于是找到了广度优先遍历寻路算法学习了下，想看看可不可以对写A*有什么帮助。广度优先遍历寻路算法本身并不难，概括来说就是像雷达一样，一层一层进行寻找目标点。当找到目标点后进行回溯。从而找到最佳路径。也就是说每走一步都要找到到达该点的最短的路径，最终得到到达所有点的最短路径。

Point.java

``````public class Point {
private int x;
private int y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}

}``````

MyMap.java

``````public class MyMap {
//记录上一个父亲位置
private int preX;
private int preY;
//记录权值
private int price;
public MyMap() {
preX = 0;
preY = 0;
price = 0;
}
public int getPreX() {
return preX;
}
public void setPreX(int preX) {
this.preX = preX;
}
public int getPreY() {
return preY;
}
public void setPreY(int preY) {
this.preY = preY;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}``````

test.java

``````import java.util.LinkedList;
import java.util.Queue;

public class test {
static  char[][] M = {
{' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
{' ',' ',' ',' ','#','#','#',' ',' ',' '},
{' ',' ',' ',' ',' ',' ','#',' ',' ',' '},
{' ',' ',' ',' ','#','E','#',' ',' ',' '},
{' ',' ',' ',' ','#','#','#',' ',' ',' '},
{' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
{' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
{'#','#','#','#','#','#','#','#','#',' '},
{' ',' ',' ',' ',' ',' ',' ',' ',' ',' '},
{'S',' ',' ',' ',' ',' ',' ',' ',' ',' '}
};
static int[] dx = { -1,0,1,1,1,0,-1,-1 };  //x方向
static int[] dy = { -1,-1,-1,0,1,1,1,0};//y方向
public static Point start = null;
public static Point end = null;

public static void main(String[] args) {

//获取起始点和结束点
for(int i = 0;i < 10;i++) {
for(int j = 0;j < 10;j++) {
if(M[i][j] == 'S') {
start = new Point(j,i);
}
if(M[i][j] == 'E') {
end = new Point(j,i);
}
}
}
//开始遍历
bfs();

//从结束点开始打印出路径
for(int i = 0;i < 10;i++) {
for(int j = 0;j < 10;j++) {
System.out.print(M[i][j]);
}
System.out.println();
}

}
/////////////广度优先部分,主要寻路在这里//////////
public static void bfs() {
//队列进行记录待遍历的点
//将起始点加入队列
//设置与地图等大的MyMap
MyMap[][] my = new MyMap[10][10];
for(int m = 0;m < 10;m++) {
for(int n = 0;n < 10;n++) {
my[m][n] = new MyMap();
}
}
//开始遍历
while(queue.size() > 0) {
//队列头进行遍历
Point p = queue.poll();
//8个方向
for(int i = 0;i < 8;i++) {
int nx = p.getX() + dx[i];
int ny = p.getY() + dy[i];

if(nx >= 0 && nx < 10 && ny >= 0 && ny < 10 && M[ny][nx] != '#') {
//第一次访问
if(my[ny][nx].getPrice() == 0) {
if((dx[i] == -1 && dy[i] == -1) || (dx[i] == -1 && dy[i] == 1) || (dx[i] == 1 && dy[i] == -1) || (dx[i] == 1 && dy[i] == 1)) {
my[ny][nx].setPrice(my[p.getY()][p.getX()].getPrice() + 14);
}else {
my[ny][nx].setPrice(my[p.getY()][p.getX()].getPrice() + 10);
}
my[ny][nx].setPreX(p.getX());
my[ny][nx].setPreY(p.getY());

}else {
//二次访问不用加入队列，
if((dx[i] == -1 && dy[i] == -1) || (dx[i] == -1 && dy[i] == 1) || (dx[i] == 1 && dy[i] == -1) || (dx[i] == 1 && dy[i] == 1)) {
if(my[p.getY()][p.getX()].getPrice() + 14 <= my[ny][nx].getPrice()) {
my[ny][nx].setPrice(my[p.getY()][p.getX()].getPrice() + 14);
my[ny][nx].setPreX(p.getX());
my[ny][nx].setPreY(p.getY());
}
}else {
if(my[p.getY()][p.getX()].getPrice() + 10 < my[ny][nx].getPrice()) {
my[ny][nx].setPrice(my[p.getY()][p.getX()].getPrice() + 10);
my[ny][nx].setPreX(p.getX());
my[ny][nx].setPreY(p.getY());
}
}
}

}

}

//////////////////以下部分只是打印不重要//////////////////
//到达目的地后
if(p.getX() == end.getX() && p.getY() == end.getY()) {
int x = p.getX();
int y = p.getY();
int tmpx = 0;
int tmpy = 0;
//打印
while(x != start.getX() || y != start.getY()) {
tmpx = my[y][x].getPreX();
tmpy = my[y][x].getPreY();
x = tmpx;
y = tmpy;
if(M[y][x] != 'S') {
M[y][x] = '*';
}
}
break;
}
}
}
}``````

Point.java里面写的就是一个点的类里面就是装的x，y没啥说的。MyMap.java写的类就是记录前一个点的位置prex 和prey其实这两个参数用一个Point来记就好了。还有一个最重要的就是那个price，记录到达当前点所需要的消耗。也就是路径长度。然后就是test.java了。test当中就是bfs函数最重要了。

将开始节点加入队列，然后在循环中先读出队列头，即出队列，读出的头就是当前节点，围绕该节点遍历周围的所有节点，分为：左上，上，右上，右，右下，下，左下，左共8个方向。然后将周围的节点依次加入到队列中，并且设置该节点的权值和前一节点坐标。不断循环重复以上操作，逐层遍历直到找到目的节点，或者队列为空，若队列为空都没有找到目标节点那么就是该节点不可达。

首先为了方便起见设置一个char类型的二维数组当做地图，大小是10*10的。然后遍历找到起始点和结束点的位置坐标，Point类型的start和end。还有dx和dy两个数组。里面存的是遍历的顺序，也就是坐标的偏移量，两个数组每个都是8个元素，因为是8个方向的嘛。然后直接进重点bfs()函数，先建立一个queue队列，将开始节点加入队列，设置与地图等大的MyMap类的一个数组，并初始化。

开始遍历，一个while循环，循环条件是queue大小要大于0，循环体中则是读出队列头，出队列，一个for循环用来遍历该点周围8个方向的点，当然要有约束条件周围的点不能超过10*10的范围同时不能为墙壁，判断墙壁用的是之前char数组的地图，而遍历记录price和前一个点的坐标的是那个MyMap数组。queue出来的点为当前点，遍历的是周围点，周围的点因为是8个方向，当扩大的时候必然会存在重复遍历的问题，不能单纯的用一个布尔来标记是否重复遍历，因为如果遍历到就进行标记的话，得到的路径不一定是最短的。因此我用的是一个price来记录到达该点的路径长度，判断是否遍历过了也很简单，如果price为0的话那么说就是第一次遍历，若不为0那就说明不是第一次遍历。两种情况分开讨论。（1）第一次遍历到该点：因为是第一次遍历到，所以要先将其加入到队列中，然后将设置消耗price，就是获取当前点（队列里出来的点）的price加上两点间路径长度，两点间路径长度：斜对角是14，上下左右相邻是10。为什么这么设，其实就是勾股定理的出来乘以10，也可以不这么设定只要相邻的距离相加大于斜对角距离就可以，满足三角形两边之和大于第三边就可以。然后设置前一点坐标为当前点。（2）第二次遍历到该点：因为不是第一次了，所以这里就不用再将其加入队列中，这里要做的就是判断应不应该与这个点连线，怎么说呢，因为他已经有price记录了，说明他已经有主子了，那么此时就要判断他指向的主子称不称职。那他的price与自己的price加上距离进行比较，如果他的price较小说明主子很称职，也就是路径较短，如果自己的price加距离要小于他自己的price说明他当前的主子不称职，就改变他的前一点的标记，改成当前点的坐标，并改变price值。如此一来可以保证到达每个点都是最小权值，按照点记录的前一点坐标进行回溯即可得到到达点的最短路径。

原文作者：BFS
原文地址: https://blog.csdn.net/qq_36571422/article/details/80357138
本文转自网络文章，转载此文章仅为分享知识，如有侵权，请联系博主进行删除。