BFS breadth-first traversal to find the shortest path (super detailed implementation process)

Breadth-first traversal to find the shortest path

      Recently, I have been trying to work on the A* algorithm, but I found that some parts were not understood clearly. So I found the breadth-first traversal pathfinding algorithm and studied it, and wanted to see if it could help in writing A*. The breadth-first traversal pathfinding algorithm itself is not difficult. In general, it is like a radar, which searches for target points layer by layer. When the target point is found, backtracking is performed. to find the best path. That is to say, every step is to find the shortest path to the point, and finally get the shortest path to all points.

Without further ado, let's talk about the code. The specific explanation is behind the code

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() {
		//队列进行记录待遍历的点
		Queue<Point> queue = new LinkedList<>();
		//将起始点加入队列
		queue.add(start);
		//设置与地图等大的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) {
						queue.add(new Point(nx,ny));
						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;
			}
		}
	}
}

Effect


Explanation of the class:

      What is written in Point.java is that the class of a point is filled with x, and y has nothing to say. The class written by MyMap.java is to record the position of the previous point, prex and prey. In fact, these two parameters can be recorded with a Point. Another most important thing is the price, which records the consumption required to reach the current point. That is, the path length. Then there is test.java. Among the tests, the bfs function is the most important.

The general idea of ​​traversal:

      Add the start node to the queue, and then read the queue head first in the loop, that is, out of the queue, the read head is the current node, and traverse all the surrounding nodes around the node, divided into: upper left, upper, upper right, right, lower right , down, left down, left a total of 8 directions. Then the surrounding nodes are added to the queue in turn, and the weight of the node and the coordinates of the previous node are set. Repeat the above operations in a loop, traversing layer by layer until the destination node is found, or the queue is empty. If the queue is empty and the target node is not found, then the node is unreachable.


Ultra-detailed implementation process of the program:

       First of all, for the sake of convenience, set a two-dimensional array of char type as a map with a size of 10*10. Then traverse to find the position coordinates of the starting point and the ending point, the start and end of the Point type. There are also two arrays dx and dy. It stores the order of traversal, that is, the offset of the coordinates. Each of the two arrays has 8 elements, because they are in 8 directions. Then go directly to the key bfs() function, first create a queue queue, add the start node to the queue, set an array of the MyMap class as large as the map, and initialize it.

      Start traversal, a while loop, the loop condition is that the queue size is greater than 0, the loop body is to read the queue head, out of the queue, a for loop is used to traverse the points in 8 directions around the point, of course, there must be constraints around The point cannot exceed the range of 10*10 and cannot be a wall. It is judged that the wall uses the map of the previous char array, and the MyMap array that traverses the record price and the coordinates of the previous point. The point coming out of the queue is the current point, and the surrounding points are traversed. Because the surrounding points are in 8 directions, there will inevitably be a problem of repeated traversal when expanding. You cannot simply use a Boolean to mark whether to repeat the traversal, because if traversed If you mark it when you arrive, the obtained path is not necessarily the shortest. Therefore, I use a price to record the length of the path to the point. It is also very simple to determine whether the traversal has been done. If the price is 0, then it is the first traversal. If it is not 0, it means that it is not the first traversal. . The two cases are discussed separately. (1) The first traversal to this point: because it is the first time to traverse, it must be added to the queue first, and then set to consume the price, which is to obtain the price of the current point (the point that came out of the queue) plus the price The length of the path between two points, the length of the path between two points: the diagonal angle is 14, and the adjacent upper, lower, left, and right sides are 10. The reason for this setting is actually the result of the Pythagorean theorem multiplied by 10. It can also be set differently, as long as the adjacent distances are greater than the diagonal diagonal distance, and the sum of the two sides of the triangle is greater than the third side. Then set the coordinates of the previous point as the current point. (2) The second traversal to this point: because it is not the first time, there is no need to add it to the queue here. What we need to do here is to judge whether it should be connected to this point. How to say, because he There is already a price record, indicating that he already has a master, so at this time it is necessary to judge whether the master he points to is incompetent. Then compare his price with his own price plus distance. If his price is smaller, it means that his master is very competent, that is, the path is shorter. If his price plus distance is smaller than his own price, it means that his current master is incompetent. , change the mark of his previous point to the coordinates of the current point, and change the price value. In this way, it can be ensured that reaching each point is the minimum weight, and backtracking according to the coordinates of the previous point recorded by the point can obtain the shortest path to the point.


After the node is found to be repeated, the consumption is judged, and if a better path is found, the coordinates and consumption values ​​are changed.


Then find the path and backtrack directly, change the map, change the passed nodes to '*' symbols, and finally print the map as a whole. is the shortest path. Compared with the A* algorithm, the path-finding efficiency of this algorithm is still relatively low. But it's still quite understandable.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324134440&siteId=291194637