首頁 > 軟體

Java資料結構BFS廣搜法解決迷宮問題

2022-04-06 19:03:39

1.例題

題目描述

迷宮由 n 行 m 列的單元格組成,每個單元格要麼是空地,要麼是障礙物。其中1表示空地,可以走通,2表示障礙物。給定起點座標startx,starty以及終點座標endx,endy。現請你找到一條從起點到終點的最短路徑長度。

輸入

第一行包含兩個整數n,m(1<=n,m<=1000)。接下來 n 行,每行包含m個整數(值1或2),用於表示這個二維迷宮。接下來一行包含四個整數startx,starty,endx,endy,分別表示起點座標和終點座標。

輸出

如果可以從給定的起點到終點,輸出最短路徑長度,否則輸出NO。 

測試資料 

輸入

5 4

1 1 2 1

1 1 1 1

1 1 2 1

1 2 1 1

1 1 1 2

輸出

7

2. 思路分析

基本思想

這道題屬於一道較為經典的BFS圖的廣度優先搜尋演演算法例題。類似於一個分層搜尋的過程,廣度優先搜尋需要使用一個佇列以保持存取過的結點的順序,以便按這個順序存取這些結點的鄰接結點。即從給定的起點開始向四周擴散,判斷是否能夠到達終點,如果不能,就繼續BFS廣搜,直到能夠到達終點或者將所有結點遍歷完一遍。在搜尋前定義一個flag變數用來標記是否到達終點。

具體步驟

1)存取初始結點 p 並標記結點 p 為已存取。

2)結點 p 入佇列

3)當佇列非空時,繼續執行,否則演演算法結束。

4)出佇列取得隊頭結點 first

5)查詢結點 first 的第一個方向的鄰接結點 newp 。

6)若結點 first 的鄰接結點 newp 不存在,則轉到步驟3:否則迴圈執行以下三個步驟:

  • 6.1若結點 newp 尚未被存取並且可以走通,則存取結點 newp 並標記為已存取。
  • 6.2結點 newp 入佇列
  • 6.3查詢結點 first 的繼 newp 鄰接結點後的下個鄰接結點 newp .轉到步驟6

程式碼實現

import java.util.LinkedList;
import java.util.Scanner;
 
public class Main {
	//n*m的地圖,(startx,starty)起點座標,(endx,endy)終點座標
	static int n, m, startx, starty, endx, endy;
	static int[][] map;//地圖
	static boolean[][] vistied;//標記當前點是否已經走過
	static int[][] step = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };
	/*
	 * 上下左右移動遍歷搜尋 1 ,0 表示 x + 1 ,y + 0 向右移動,其他同理 如果為八向連通 則加上, { 1, 1 }, { 1, -1 }, {
	 * -1, 1 }, { -1, -1 } 代表斜向移動
	 */
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		m = sc.nextInt();
		map = new int[n + 2][m + 2];//+2是為了防止點在邊界時,四方向移動造成下標出現-1越界。
		vistied = new boolean[n + 2][m + 2];
		// 錄入資料圖  下標(1~n)*(1~m)
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				map[i][j] = sc.nextInt();
			}
		}
		//錄入起點和終點座標
		startx = sc.nextInt();
		starty = sc.nextInt();
		endx = sc.nextInt();
		endy = sc.nextInt();
		bfs(startx, starty);//BFS廣搜
	}
 
	private static void bfs(int x, int y) {
		point p = new point(x, y, 0);//初始化point類封裝x,y座標以及step步數
		LinkedList<point> queue = new LinkedList<point>();//需要用到的佇列
		queue.add(p);//將當前點入佇列
		vistied[x][y] = true;//標記成已存取
		boolean flag = false;//是否達到終點標記
		//只要佇列不為空,就說明還有路可走
		while (!queue.isEmpty()) {
			point first = queue.getFirst();//取出佇列的第一個點
			if (first.x == endx && first.y == endy) {//判斷當前點是否與終點座標重合
				System.out.println(first.step);//列印需要走的步數
				flag = true;//標記已經到達終點
				break;//結束BFS
			}
			//未到達終點則進行上下左右四個方向移動
			for (int i = 0; i < 4; i++) {
				//橫縱座標變換實現位置移動
				int newx = first.x + step[i][0];
				int newy = first.y + step[i][1];
				int newstep = first.step + 1;//每一動一次步數要+1
				//判斷移動後的新位置可以走,並且沒走過
				if (map[newx][newy] == 1 && vistied[newx][newy] == false) {
					point newp = new point(newx, newy, newstep);//封裝資料
					queue.add(newp);//入佇列
					vistied[newx][newy] = true;//標記已經走過
				}
			}
			queue.removeFirst();//四個方向判斷完成後,要將隊首元素出列
		}
		if (!flag)//如果無法到達終點提示
			System.out.println("NO");
	}
}
//定義一個位置點的class,方便封裝資料入佇列
class point {
	int x;
	int y;
	int step;//從起點走到當前點需要走的步數
 
	public point(int x, int y, int step) {
		this.x = x;
		this.y = y;
		this.step = step;
	}
}

3.BFS小結

求解思路:

將隊首結點可拓展的點入隊,如果沒有可拓展的點,將隊首結點出隊。重複以上步驟,直到到達目標位置或者佇列為空。

注意

bfs先找到的一定是最短的。但是如果是加權邊的話這樣就會出問題了,bfs傳回的是經過 邊數 最少的解,但是因為加權了,這個解到根節點的 距離 不一定最短。 比如1000+1000是隻有兩段,1+1+1+1有4段,由於bfs返回的經過邊數最少的解,這裡會返回總長度2000的那個解,顯然不是距離最短的路徑 。BFS 運用到了佇列。

到此這篇關於Java資料結構BFS廣搜法解決迷宮問題的文章就介紹到這了,更多相關Java 迷宮問題內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com