看完解决所有的【最短路径】问题!!!

1,846 阅读2分钟

起因

5月8号刚过完一个愉快地母亲节,今天刷图论题时遇到这样一个困难题127. 单词接龙,这让我一下子就想起了,5月7号的力扣每日一题433. 最小基因变化。这两题简直是一模一样,一样的难!

回想起前天的连跪战绩,于是俺决定痛定思痛地狠狠修炼一波最短路径类型题的解决办法,以及寻找出JavaScript版的解题模板。 image.png

入局

对于以上两个题目,起点为一个字符串,终点也为一个等长字符串,还有一个路径数组,保存着所有中间地点(一般包含终点)。

个人思路如下:从起点出发,每走一步记录到达位置的距离,不回头,直到到达终点。为何这样可以得出最短?

因为,当到达终点后就不会再管其他还没到终点的路,只要到达终点了,就已经是最短最快的了,其他的路(也可以到终点)就没有意义了。

那为啥不用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
}

然而按照自己写的去做题,虽然过了,但是复杂度较高 image.png f963a3e371776f9512706f3da954e18c.jpeg

破局

于是我想到一个好办法,那就是去看提交记录中超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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

image.png

感悟

BFS的关键点在于每走一步,探索下一步的路,同时走多个分叉路,不回头,直到终点\color{red}{每走一步,探索下一步的路,同时走多个分叉路,不回头,直到终点}

优化的关键则在于同时从起点和终点出发,岔路少的先走,直到相遇!双向奔赴yyds\color{red}{同时从起点和终点出发,岔路少的先走,直到相遇!双向奔赴yyds}

厮杀

image.png

1091. 二进制矩阵中的最短路径

image.png

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 矩阵

image.png

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
};

更多类似图论问题,点击这里查看