起因
5月8号刚过完一个愉快地母亲节,今天刷图论题时遇到这样一个困难题127. 单词接龙,这让我一下子就想起了,5月7号的力扣每日一题433. 最小基因变化。这两题简直是一模一样,一样的难!
回想起前天的连跪战绩,于是俺决定痛定思痛地狠狠修炼一波最短路径类型题的解决办法,以及寻找出JavaScript版的解题模板。
入局
对于以上两个题目,起点为一个字符串,终点也为一个等长字符串,还有一个路径数组,保存着所有中间地点(一般包含终点)。
个人思路如下:从起点出发,每走一步记录到达位置的距离,不回头,直到到达终点。为何这样可以得出最短?
因为,当到达终点后就不会再管其他还没到终点的路,只要到达终点了,就已经是最短最快的了,其他的路(也可以到终点)就没有意义了。
那为啥不用DFS呢?也正是因为,DFS要走完每一条能到终点的路,然后再比较哪一条最近最短,这样会走一些重复的路。
var bfs = (start, end, midlist) => {
// 终点如果不在地点数组里就直接返回-1
// if (!midlist.includes(end)) return -1
let dires = new Set(midlist) //地点集
const len = dires.size //注意每个都要遍历到,固定不变
let path = new Map() //记录到达每个地点的距离,[地点,起点到该地点距离]
path.set(start, 0) //初始化起点
// 遍历每一个地点集
for (let i = 0; i < len; i++) {
for (let item of path) {
// 通过判断当前走了几步,来确定下一步怎么走
if (item[1] == i) {
// 获取当前地点位置
let cur = item[0]
// 找走一步就能到的地点,循环同时走
for (let dir of dires) {
let count = 0
for (let i = 0; i < start.length; i++) {
(dir[i] !== cur[i]) && count++
}
// 走一步能到地点就记录
if (count == 1) {
if (dir === end) return path.get(cur) + 1
path.set(dir, path.get(cur) + 1)
// 来过就删除,以后不用来了
dires.delete(dir)
}
}
}
}
}
// 只要没有走或者没有走到终点就返回-1
return -1
}
然而按照自己写的去做题,虽然过了,但是复杂度较高
破局
于是我想到一个好办法,那就是去看提交记录中超100%的代码。
var ladderLength = function (start, end, wordList) {
let dires = new Set(wordList), Me = new Set(), You = new Set()
// 如果我们不可能相遇,那一切都没有意义
if (!dires.has(end)) return 0
Me.add(start)
You.add(end)
const dirlen = start.length //地点名长度
let step = 1
while (Me.size) {
// 双向奔赴,谁的岔路比较少,谁就先走
if (Me.size > You.size)
[Me, You] = [You, Me]
const Nextpath = []
for (let curSelect of Me) {
// 寻找下一个地点
for (let i = 0; i < dirlen; i++) {
for (let c = 97; c <= 122; c++) {
const newSelect = curSelect.slice(0, i) + String.fromCharCode(c) + curSelect.slice(i + 1);
// 如果你是我的下一个选择就好了
if (You.has(newSelect)) return step + 1
if (dires.has(newSelect)) {
// 记录下一个可以到的地方
Nextpath.push(newSelect)
// 不走回头路
dires.delete(newSelect)
}
}
}
}
// 下一步,从新开始
Me = new Set(Nextpath)
// 我们一起走的步数
step++
}
return 0
};
作者:qipao
链接:https://leetcode.cn/problems/word-ladder/solution/javascript-by-qipao-q7qw/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
感悟
BFS的关键点在于:
优化的关键则在于:
厮杀
1091. 二进制矩阵中的最短路径
const dirs = [[0, 1], [0, -1], [1, 1], [1, -1], [-1, 0], [-1, 1], [1, 0], [-1, -1]]
var shortestPathBinaryMatrix = function (grid) {
let start = new Set(), n = grid.length
if (grid[0][0] || grid[n - 1][n - 1]) return -1
start.add([0, 0])
let step = 1
if (n === 1 && grid[0][0] === 0) return step
while (start.size) {
let nextpath = []
for (let cur of start) {
let x = cur[0], y = cur[1]
for (let dir of dirs) {
let X = x + dir[0], Y = y + dir[1]
if (X === n - 1 && Y === n - 1) return step + 1
if (X >= 0 && X < n && Y >= 0 && Y < n && !grid[X][Y]) {
nextpath.push([X, Y])
grid[X][Y] = 1
}
}
}
start = new Set(nextpath)
step++
}
return -1
};
542. 01 矩阵
var updateMatrix = function (mat) {
const m = mat.length, n = mat[0].length,dirs = [[-1, 0], [1, 0], [0, -1], [0, 1]]
let res = new Array(m).fill(0).map(v => new Array(n).fill(-1)), set = new Set(), step = 0
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (mat[i][j] === 0) {
res[i][j] = 0
set.add([i, j])
}
}
}
while (set.size) {
let next = []
for (let cur of set) {
let x = cur[0], y = cur[1]
for (let dir of dirs) {
let X = dir[0] + x, Y = dir[1] + y
if (X >= 0 && X < m && Y >= 0 && Y < n && res[X][Y] === -1) {
res[X][Y] = step + 1
next.push([X, Y])
}
}
}
set = new Set(next)
step++
}
return res
};
更多类似图论问题,点击这里查看