1. 程式人生 > >走迷宮問題廣度優先演算法實現

走迷宮問題廣度優先演算法實現

走迷宮問題

迷宮是許多小方格構成的矩形,在每個小方格中有的是牆(用1表示),有的是路(用0表示)。走迷宮就是從一個小方格沿上、下、左、右四個方向到鄰近的方格,當然不能穿牆。

問題描述
實現生成迷宮(從文字中讀取資料生成),輸出迷宮(圖案方式),探索迷宮路徑(最短路徑),輸出迷宮路徑(圖案方式)。

輸入的檔案內容格式為:

6 5
0 1 0 1 1
0 0 1 1 1
1 0 0 1 1
0 1 0 0 1
1 1 1 0 0
1 1 1 1 0

開頭一行表示地圖為6行5列,地圖中0表示路,1表示障礙物。現在規定從左上角進入迷宮從右下角出迷宮。

演算法分析
在任意位置,理論上有四個位置可以走,上下左右。現在位於位置0,走一步可以走的位置為:

也就是當前位置為0,可以走的位置為1,相同道理,走2步可以走的位置為:

當位於0時,可以探索的位置為周圍的四個1,廣度優先的演算法是根據狀態可分為兩種狀態,當前位置和下一步可以為的位置,當前位置為0,下一步可以為的位置為1,通過佇列將下一步位置先儲存起來,遍歷下一步可以為的4個1位置,再分別根據位於4個1位置時探索下一步的位置。如果當位於0位置找到一個下一步位置1之後再直接進行下一步探索則成了深度優先演算法。

以起始點為出發,將這個點按照某個方向規則走,周圍所有可以走的位置依次寫入到佇列,這個點走完之後,隊首出佇列,走這個點四周所有可以走的點,按照之前的規則,將周圍可走的點依次放入到佇列中,重複之前過程,直到佇列為空或者走到終點。在這個過程中在藉助一個輔助矩陣,存放起點到位置的最短路徑。

原始碼分析

//座標結構
type point struct {
	i int
	j int
}

//分別代表上左下右
var dirs = [4]point{
	{-1, 0},
	{ 0, -1},
	{ 1, 0},
	{ 0, 1},
}

//讀取地圖檔案
func readMaze(fileName string) [][]int {
	file, err := os.Open(fileName)
	if err != nil {
		panic("open file fail")
	}

	var row, col int
	var changeLine int
	fmt.Fscanf(file,
"%d %d", &row, &col) maze := make([][]int, row) for i := range maze { maze[i] = make([]int, col) //讀取檔案過程中遇到換行讀取為0 fmt.Fscanf(file, "%d", &changeLine) for j := range maze[i] { fmt.Fscanf(file, "%d", &maze[i][j]) } } return maze } func walkMaze(maze [][]int, start, end point)([][]int,bool) { steps := make([][]int, len(maze)) for i := range steps { steps[i] = make([]int, len(maze[0])) } //儲存四周下一步可以出現位置 Q := []point{start} for len(Q) > 0 { cur := Q[0] Q = Q[1:] for _, dir := range dirs { next := cur.add(dir) //1代表障礙物 nextValue, ok := next.at(maze) if !ok || nextValue == 1 { continue } //下一步必須是還沒走過的位置 nextValue, ok = next.at(steps) if !ok || nextValue != 0 { continue } //不能走到其實位置 if next == start { continue } Q = append(Q,next) mazeValue, ok := cur.at(steps) if ok { steps[next.i][next.j] = mazeValue + 1 } } } //當走到終點時,則終點位置會被賦值為走過的步數 if steps[len(steps)-1][len(steps[0])-1] == 0 { return steps,false } return steps,true }

完整原始碼