迷宮的廣度優先搜尋
一、 廣度優先演算法
迷宮的廣度優先搜尋是基於廣度優先演算法來實現的,在爬蟲領域也會經常使用廣度優先演算法,首先來了解一下廣度優先演算法。
我在之前對文章中具體介紹了廣度優先演算法,可參見[圖論系列之「廣度優先遍歷及無權圖的最短路徑(ShortPath)」,這裡我們需要使用一個輔助佇列,用來存放訪問過的節點,下圖是我們的迷宮例項(6行5列):
從左上角為入口,右下角為出口,數字1表示牆(走不通),數字0表示通道,這裡我們需要規定節點的遍歷方向,即:上左下右(逆時針),下面來具體介紹廣度優先演算法在迷宮中的邏輯:
當前節點為0(入口),將該0放入輔助佇列中,然後拿出節點0並標記,再將該0節點相鄰的節點以上左下右(逆時針)的順序放入佇列中,此時的佇列中,佇列:0、1
接著從佇列中拿出0,在將該0相鄰的節點(0、0)放入佇列中,此時的佇列:1、0、0
接著再拿出佇列中的第一個節點”1”,但節點1是牆,將其捨棄此時的佇列:0、0
再拿出佇列中的第一個節點”0”,然後將0節點相鄰的節點1、1放入佇列中,此時的佇列:0、1、1
5接著再拿出佇列中第一個節點“0”,而該節點相鄰的節點有:1、0、1、0
此時只有該0節點的右鄰節點未被訪問,即:0,放入佇列,此時的佇列:0、1、1、1
……
……
以此類推,就可找到出口二、檔案讀入
我們需要將:
6 5 0 1 0 0 0 0 0 0 1 0 0 1 0 1 0 1 1 1 0 0 0 1 0 0 1 0 1 0 0 0
讀入,並建立一個6行5列的二維slice
程式如下://讀入檔案 func ReadMaze(filename string) [][]int{ file, err := os.Open(filename) if err != nil{ panic(err) } var row, col int fmt.Fscanf(file, "%d %d", &row, &col) //fmt.Fscanf()? maze := make([][]int, row) for i := range maze{ maze[i]= make([]int, col) for j := range maze[i]{ fmt.Fscanf(file, "%d", &maze[i][j]) } } return maze }
三、廣度優先演算法的實現
我們需要將數字抽象為節點:
type point struct { i, j int }
還需要對每一個節點的相鄰節點進行訪問:
var dirs = [4]point{ {-1,0}, {0,-1}, {1,0}, {0,1}}
這是廣度優先演算法的核心:
func walk(maze [][]int, start, end point) [][]int{ //建立迷宮正確路線 steps := make([][]int, len(maze)) for i := range steps{ steps[i] = make([]int, len(maze[i])) } //建立佇列Q Q := []point{start} for len(Q) > 0 { cur := Q[0] Q = Q[1: ] if cur == end{ break } for _, dir := range dirs{ next := cur.add(dir) val, ok := next.at(maze) if !ok || val == 1{ //1為牆,ok == fals為越界 continue } val, ok = next.at(steps) //判斷是否被訪問,steps中的值為零表示為牆 if !ok || val != 0 { continue } if next == start{ continue } curSteps, _ := cur.at(steps) steps[next.i][next.j] = curSteps + 1 Q = append(Q, next) //maze at next is 0 //and steps at next is 0 指到過的點 //and next == 0 } } return steps }
我們還需要對座標進行變更新:
//座標變換 func (p point)add(r point) point{ return point{p.i + r.i, p.j + r.j} }
在遍歷過程中需要檢查是否越界:
//判斷越界 func (p point)at(grid [][]int)(int, bool){ if p.i < 0 || p.i >= len(grid) { return 0, false } if p.j < 0 || p.j >= len(grid[p.i]){ return 0, false } return grid[p.i][p.j],true }
四、呼叫
func main() { maze := ReadMaze("maze/maze.in") steps := walk(maze, point{0,0}, point{len(maze)-1, len(maze[0])-1}) for i := range steps{ for j := range steps[i]{ fmt.Printf("%3d " ,steps[i][j]) } fmt.Println() } }
列印結果:
0 0 4 5 6
1 2 3 0 7
2 0 4 0 8
0 0 0 10 9
0 0 12 11 0
0 0 13 12 13
本作品採用《CC 協議》,轉載必須註明作者和本文連結