迷宫的求解是非常常见的问题了,今天这里记录使用go语言来完成广度优先遍历
要求时从文件中读出一个数字矩阵,0表示可以通行,1表示不可以。要求给出一条路径从左上角到右下角,使得路径的长度最短,可以使用6来标识路径,比如:
这里需要说明的是,既然是找到最短路径,必须使用广度优先搜索来进行实现,广度优先搜索或许没有深度优先搜索快,但是找到的路径肯定是最短的,在上面的例子中其实已经展示的比较明显, 其实是有两条路可以走的,但是程序走了短的一条,关于具体的实现思路,在我的java实现中已经讲得非常详细了
在这里呢,其实也是使用额外的一个切片来记录中间过程,当有可以走的通路时,就在前一个通路的基础上加1,全程使用队列来进行广度遍历,中间表如下:
代码如下:
读取文件中的矩阵:
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)
maze := make([][]int, row)
for i := range maze {
maze[i] = make([]int, col)
fmt.Fscanf(file,"%d")
for j := range maze[i] {
fmt.Fscanf(file,"%d",&maze[i][j])
}
}
return maze
}
走迷宫的方法
func run(maze [][]int, start point, end point) [][]int{
//生成steps矩阵
steps := make([][]int, len(maze))
for i := range steps {
steps[i] = make([]int,len(maze[i]))
}
//生成队列,并把第一个节点放入队列
Q := []point{{0,0}}
//如果队列不为空,说明没有结束
for len(Q) > 0 {
//取出队头元素,并删除
peek := Q[0]
Q = Q[1:]
//把该元素的上下左右都要走一步,看看是否走得通
for _,dir := range dirs {
next := peek.add(dir)
if next.isNotAccess(steps,maze,start) {
continue
}
//给steps赋值
steps[next.i][next.j] = steps[peek.i][peek.j] + 1
//放入队列
Q = append(Q,next)
}
}
return steps
}
定义坐标,上下左右,以及判断是否可以走
//定义坐标结构体
type point struct {
i,j int
}
//定义当前元素的上下左右元素的差值
var dirs = [4]point {{-1,0},{1,0},{0,-1},{0,1}}
//定义next走差值的动作
func(p point) add(dir point) point{
return point{p.i+dir.i,p.j+dir.j}
}
//判断next走不通的情况:1.遇到障碍,出边界 2.已经走过了,不能走回去 3.不能转个圈走回去了
func(next point) isNotAccess(steps [][]int,maze [][]int,start point) bool{
if next.i < 0 || next.i >= len(maze) {
return true
}
if next.j < 0 || next.j >= len(maze[0]) {
return true
}
if maze[next.i][next.j] == 1 {
return true
}
if steps[next.i][next.j] != 0 {
return true
}
if next.i == start.i && next.j == start.j {
return true
}
return false
}
打印迷宫路径
//从右下角往上面找,如果是少1,则为路径节点,
func changeMaze(maze [][]int, steps [][]int) [][]int{
var cur = point{len(maze)-1,len(maze[0])-1}
var start = point{0,0}
for cur != start{
//找四周节点,是否为当前节点的值-1
for _, dir := range dirs {
next := cur.add(dir)
//判断是否合法
if next.i >= 0 && next.i < len(maze) && next.j >= 0 && next.j < len(maze[0]) &&
steps[next.i][next.j] == steps[cur.i][cur.j]-1 {
//修改maze为6
maze[cur.i][cur.j] = 6
cur = next
}
}
}
maze[0][0] = 6
return maze
}
打印迷宫路径就是根据中间表,倒着找,这种结构类似于树,虽然从前往后可能出现多个分支,但是从后往前只有一个
main方法
func main() {
//读出maze矩阵
maze := readMaze2("maze/maze.in")
//打印maze
fmt.Println("maze:")
for i := range maze {
for j := range maze[0] {
fmt.Printf("%4d",maze[i][j])
}
fmt.Println()
}
//开始走迷宫run. 从哪点到哪点
steps := run(maze, point{0, 0}, point{len(maze) - 1, len(maze[0]) - 1})
//根据steps来给出一条通路
maze = changeMaze(maze, steps)
fmt.Println("steps:")
for i := range steps {
for j := range steps[i] {
fmt.Printf("%4d",steps[i][j])
}
fmt.Println()
}
fmt.Println("changed maze:")
for i := range maze {
for j := range maze[i] {
fmt.Printf("%4d",maze[i][j])
}
fmt.Println()
}
}
最后的结果: