go语言实现最短路径走迷宫算法

8 篇文章 0 订阅

今天看了一个介绍走迷宫算法的视频,就自己动手写了一遍。写一篇日志以免将来忘记。

首先是迷宫的表示。如左下图,起始位置是左上角黄色位置,重点位置是右下角黄色位置。在这个二维矩阵里,0表示道路通畅,可走;1表示有障碍物,不可走。最终计算出来的结果如右下角所示,从左上角0开始,每走一步,累加一次,这样就可以显示出整条路径的先后顺序。要走最短路径,只要从终点位置,不断递减1寻找上一步的位置直到回到起始位置即可。

算法的主要过程:

从矩阵中一点向外探索,总共有四个方向,分别是上下左右,如下图。

当探索了第一步以后,可以继续向外探索,如从上开始,第二步探索只有三个方向,因为根据规则不能往回走。

往上探索结束后,往左探索。

往左探索结束后往下探索。

往下探索结束后往右探索

这样就形成了一圈逆时针方向的探索。

继续进行第三步探索,如下图所示。

一直持续这个过程直到走到终点。

这里要注意的一点是,一定要先探索完1号所有位置后,再探索完2号所有位置。这叫广度优先,不然就成了深度优先了,就得不到最短路径了。

这里我使用队列来存储下一步可走的位置实现广度优先算法。代码如下:

package main

import (
	"fmt"
	"os"
)

func readMaze(filename string) [][]int {
	file, err :=os.Open(filename)
	defer file.Close()
	if err != nil {
		panic(err)
	}
	var row,col int
	_, err = fmt.Fscanf(file,"%d %d\n", &row, &col)
	if err != nil {
		panic(err)
	}
	var maze = make([][]int, row)
	for i:=range maze {
		maze[i] = make([]int, col)
		for j:= range maze[i] {
			_, err = fmt.Fscanf(file,"%d", &maze[i][j])
			if err != nil {
				panic(err)
			}
		}
		var ln string
		fmt.Fscanln(file, &ln)
	}

	return maze
}

type point struct {
	i,j int
}


//向某个方向探索
func (p point)Explore(direc direction) point {
	return point{p.i+direc.i, p.j+direc.j}
}

//判断一个点是否落在迷宫地图内
func (p point)InMaze(maze [][]int) bool {
	if p.i < 0 || p.i >=len(maze) {
		return false
	}
	if p.j < 0 || p.j >= len(maze[0]) {
		return false
	}
	return true
}

//判断一个点是否落在障碍物上
func (p point)OnObstacle(maze [][]int) bool {
	if maze[p.i][p.j] == 1 {
		return true
	}
	return false
}

func (p point)IsValidPoint(maze [][]int) bool {
	if !p.InMaze(maze) {
		return false
	}
	if p.OnObstacle(maze) {
		return false
	}
	return true
}

//判断这个点是不是在往回走
func (p point)IsBack(steps [][]int, start point) bool {
	if steps[p.i][p.j] != 0 {
		return true
	}

	//注意,起始点也是0,所以要把起始点也排除在外
	if p.i == start.i && p.j == start.j {
		return true
	}
	return false
}

type direction point
var FourDirections = [4]direction {
	{-1,0}, {0,-1}, {1,0},{0,1},
}

//返回路径地图和路径结果(true表示找到了最佳路径)
func walk(maze [][]int, start,end point) ([][]int, bool){
	//构造路径地图
	steps := make([][]int, len(maze))
	for i:= range steps {
		steps[i] = make([]int, len(maze[0]))
	}

	//广度优先搜索队列
	var q =[]point{start}
	for {
		//判断是否已经找到最佳路径
		if len(q) == 0 {
			return steps,false
		}

		//取出队列首个元素为当前点
		cur := q[0]
		q = q[1:]

		if cur.i == end.i && cur.j == end.j {
			return steps,true
		}

		//求出四个方向上的点
		for _, direc := range FourDirections {
			nextPoint := cur.Explore(direc)

			//查看探索到的点是否合法
			//1. 在迷宫地图范围内
			//2. 有没有落在墙上
			//3. 没有走回头路
			if !nextPoint.IsValidPoint(maze) {
				continue
			}
			if nextPoint.IsBack(steps, start) {
				continue
			}

			//合法的step就添加到广度优先搜索队列里去
			q = append(q, nextPoint)
			//在路径地图上打路径tag
			steps[nextPoint.i][nextPoint.j] = steps[cur.i][cur.j]+1
		}

	}

	return steps,false
}

func PrintSteps(steps [][]int) {
	for i:= range steps {
		for j:= range steps[i] {
			fmt.Printf("%2d ",steps[i][j])
		}
		fmt.Println("")
	}
}

//找出最佳路径,注意这里找到的路径顺序是反着的
func FindBestPath(steps [][]int, start,end point) ([]point, bool){
	var path = []point{end}
	cur := end
	for steps[cur.i][cur.j] != 0 {
		//向四个方向去搜索
		for _,direc := range FourDirections {
			lastStep := cur.Explore(direc)
			//判断搜索的点是否在地图内
			if !lastStep.InMaze(steps) {
				continue
			}
			//判断搜索的点是否是上一步
			if steps[lastStep.i][lastStep.j] == steps[cur.i][cur.j]-1 {
				path = append(path, lastStep)
				cur = lastStep
				break
			}
		}
	}
	if cur.i == start.i && cur.j == start.j {
		return path,true
	}
	return path,false
}

func main() {
	var maze = readMaze("maze.in")
	//fmt.Println(maze)
	var start = point{0,0}
	var end = point{5,4}
	steps, find := walk(maze, start, end)
	if find {
		fmt.Println("find the best path.")
	} else {
		fmt.Println("no path to the end.")
	}
	PrintSteps(steps)
	path, _ := FindBestPath(steps, start, end)
	fmt.Println(path)

}


测试数据如下:

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

(全文完)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值