实训笔记二 | 青训营

113 阅读4分钟

实训笔记二

走迷宫

一、我们首先要知道什么是广度优先算法?广度优先算法能做什么?

广度优先算法(Breadth-First Search),同广度优先搜索,又称作宽度优先搜索,或横向优先搜索,简称BFS,是一种图形搜索演算法。广度优先搜索算法(又称宽度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。

广度优先算法的核心思想是:从初始节点开始,应用算符生成第一层节点,检查目标节点是否在这些后继节点中,若没有,再用产生式规则将所有第一层的节点逐一扩展,得到第二层节点,并逐一检查第二层节点中是否包含目标节点。若没有,再用算符逐一扩展第二层的所有节点……,如此依次扩展,检查下去,直到发现目标节点为止。

⒈从图中的某一顶点V0开始,先访问V0;

⒉访问所有与V0相邻接的顶点V1,V2,......,Vt;

⒊依次访问与V1,V2,......,Vt相邻接的所有未曾访问过的顶点;

⒋循此以往,直至所有的顶点都被访问过为止。

这种搜索的次序体现沿层次向横向扩展的趋势,所以称之为广度优先搜索。

我们会经常碰到这样一个问题,从一个起点A开始要到一个终点B,我们要找寻一条最短的路径。这个时候广度优先算法就用上了,为此,我们可以得到2个问题。

问题一:起点A是否有路径到达终点B?

问题二:起点A到达起点B的最短路径是什么?

通过上面的个问题我们来实现我在视屏里学习的用这个算法来走出迷宫(我也是照着视屏敲的.........)

给定一个[m][n]int矩形,每个元素上面是0或者1;0表示此路可过;1表示此路不能过;每次只能上下左右的走一格.给定一个start起点和end终点,记录从start最少走多少步可以走到end,如果走不通返回-1(给定的start和end不会有索引越界的情况)

思路:初始化一个与参数矩形长度一样的[][]int steps用来记录当前节点我们是否走过.在创建一个我们准备开始走的节点数组.将start加入到里面;循环的开始上下左右的走,走到的节点如果越界或者已经走过或者这个节点不允许走就跳过此次循环,不然就把当前节点加入到我们准备走的节点数组里面并再steps对应的节点上记录我们走到这个节点的步数.如果我们走到终点end或者已经把整个迷宫都走完了结束掉整个循环

代码实现

package main
 
import (
	"fmt"
	"os"
)
 
func main() {
	maze := readMaze("maze/maze.in") //读取迷宫文件

传入迷宫地图,起点坐标[0,0]终点坐标[]

	data := walk(maze, point{0, 0}, point{len(maze) - 1, len(maze) - 1})
 
	for _, row := range data {
		for _, val := range row {
			fmt.Printf("%3d", val)
		}
		fmt.Println()
	}
}

作用:坐标结构体

type point struct {
	i, j int
}
 

作用:下一个节点的坐标


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) {
	//验证X轴是否越界
	if p.i < 0 || p.i >= len(grid) {
		return 0, false
	}
 
	//验证Y轴是否越界
	if p.j < 0 || p.j >= len(grid[p.i]) {
		return 0, false
	}
 
	return grid[p.i][p.j], true
}

作用:当前坐标对应的周围坐标,上下左右

var dirs = [4]point{
	{0, 1}, {-1, 0}, {0, -1}, {1, 0},
}
 

func walk(maze [][]int, start, end point) [][]int {
	//初始化来记录等下要走的路
	steps := make([][]int, len(maze))
	for i := range steps {
		steps[i] = make([]int, len(maze[i]))
	}
 
	//需要走的路,起始第一步,【0,0】
	q := []point{start}

开始走

	for len(q) > 0 {
		cur := q[0] //获得起点的坐标(当前坐标)
		q = q[1:]   //删除当前的位置(当前坐标)
 
		for _, dir := range dirs {
			next := cur.add(dir) //下一个位置的坐标,因为有4个
 
			//是否越界
			val, ok := next.at(maze)
			if !ok || val == 1 {
				continue
			}
 
			//是否走过
			val, ok = next.at(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)
 
		}
 
	}
	return steps
}
 

作用:读取文件数据

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)
		for j := range maze[i] {
			fmt.Fscanf(file, "%d", &maze[i][j])
		}
	}
 
	return maze
}