1.题目描述
2. 题解
func findShortestWay(_ maze: [[Int]], _ ball: [Int], _ hole: [Int]) -> String {
// 行:row 列:column
let row = maze.count
let column = maze[0].count
// 1.用一个行,列相同的数组,维护步数
var steps = [[Int]](repeating: [Int](repeating: Int.max, count: column), count: row)
// 2.用一个行,列相同的数组,维护表示路径的字符串
var pathStrs = [[String]](repeating: [String](repeating: "impossible", count: column), count: row)
// 3.用一个元组,表示方向和字符
let dirs = [(-1,0,"u"),(1,0,"d"),(0,-1,"l"),(0,1,"r")]
// 4.用一个数组,来表示队列的数据结构,里面装的是数组,表示当前的x,y坐标,初始值为球出发的坐标
var queue = [[ball[0],ball[1]]]
// 5.初始化球出发的坐标时候,步数和对应的路径字符,步数为0,路径为空字符
steps[ball[0]][ball[1]] = 0
pathStrs[ball[0]][ball[1]] = ""
while !queue.isEmpty {
// 取出当前球所在的坐标
let ballPosition = queue.removeFirst()
// 如果当前球的位置在洞的位置,那么结束,后面再继续走,就是浪费时间和性能了
if ballPosition[0] == hole[0] && ballPosition[1] == hole[1] {
return pathStrs[ballPosition[0]][ballPosition[1]]
}
// 能来到这里,说明球所在的位置不是洞的位置
// 模拟球走的4个方向
for (x,y,str) in dirs {
// 走的下一个方向
var nextRow = ballPosition[0] + x
var nextColumn = ballPosition[1] + y
// 当前的位置的步数
var step = steps[ballPosition[0]][ballPosition[1]]
// 当前的字典序
var dictSequence = pathStrs[ballPosition[0]][ballPosition[1]]
// 假设朝着当前方向走,是有效(1.没有越界 2.是空地);
// 就继续朝着这个方向走,看能走到多少步停下(1.越界 2.撞到墙 3.进洞)
// 在横冲直撞的过程,看下上一个位置是否是洞的位置:!(nextRow - x == hole[0] && nextColumn - y == hole[1])
while nextRow >= 0 && nextRow < row && nextColumn >= 0 && nextColumn < column && maze[nextRow][nextColumn] == 0 && !(nextRow - x == hole[0] && nextColumn - y == hole[1]) {
nextRow = nextRow + x
nextColumn = nextColumn + y
// 每继续走一步,步数增加1
step = step + 1
}
// 到了这里,说明上面一步中,遇到了不符合条件的坐标
// 回退一步,就是最后一个符合坐标的位置了,我们用lastPosition来描述
nextRow = nextRow - x
nextColumn = nextColumn - y
// 更新字典序
dictSequence = dictSequence + str
// 需要尝试重新记录(1.步数 2.方向字符)
// 1.如果之前lastPosition位置的的步数数组中记录的步数比当前计算的步数大,需要更新
// 2.如果步数相等,但是字典序更大,也要重新更新,因为我们要字典序最小的路径
if steps[nextRow][nextColumn] > step || (steps[nextRow][nextColumn] == step && pathStrs[nextRow][nextColumn] > dictSequence) {
steps[nextRow][nextColumn] = step
pathStrs[nextRow][nextColumn] = dictSequence
// 不在洞里,加入队列,作为下一轮的while循环的起点
if !(nextRow == hole[0] && nextColumn == hole[1]) {
queue.append([nextRow,nextColumn]) // 在这一轮的for循环里,理论上最多增加4个元素
}
}
}
}
return pathStrs[hole[0]][hole[1]]
}
3.思路
- 计算最短路径的,用BFS思路会比DFS快几十倍,记住这个套路就行,理解因人而异。
- BFS离不开队列,入队,出队,在swift语言中,用数组就可以当做队列来使用
- 记忆化方法,在这里维护两个行、列相同的数组,分别用来记录步数、路径字符串
- 审题是要注意,这里我们可以把它想象成打台球,但是这里的台球不会反弹,因此在空地上,没有停下的时候,会继续走,我们步数是依然记录,但是方向只记录打台球“出杆”的方向(我们的台球只会“横冲直撞”)
- 队列入队的条件,当台球停下来的时候,只要台球不是进洞了,我们就入队。
- // 在横冲直撞的过程,看下上一个位置是否是洞的位置
// 在横冲直撞的过程,看下上一个位置是否是洞的位置:!(nextRow - x == hole[0] && nextColumn - y == hole[1])
while nextRow >= 0 && nextRow < row && nextColumn >= 0 && nextColumn < column && maze[nextRow][nextColumn] == 0 && !(nextRow - x == hole[0] && nextColumn - y == hole[1]) {
nextRow = nextRow + x
nextColumn = nextColumn + y
// 每继续走一步,步数增加1
step = step + 1
}
4.提交结果