JavaScript:使用 BFS 获取二进制矩阵中的最短路径

问题描述

我一直在使用 JavaScript 研究 leetcode,我试图解决这个问题 https://leetcode.com/problems/shortest-path-in-binary-matrix/

这个问题要求返回距离或最短路径的长度,我解决了。这是我的答案

var shortestPathBinaryMatrix = function(grid) {
    if(grid[0][0] != 0) return -1
    const queue = [[[0,0],1]]
    const dest = [grid.length - 1,grid[0].length - 1]
    const visited = new Set()
    const getNextSteps = ([x,y]) => {
        const dirs = [[1,[-1,[0,1],-1],[1,-1]]
        const nextSteps = []
        for(const [nx,ny] of dirs) {
            if(grid[x + nx]?.[y + ny] == 0) nextSteps.push([x + nx,y + ny])
        }
        
        return nextSteps
    }
    
    for(const [curr,distance] of queue) {
        if(visited.has(curr.toString())) continue
        if(curr[0] === dest[0] && curr[1] === dest[1] && grid[dest[0]][dest[1]] == 0) return distance
        visited.add(curr.toString())
        getNextSteps(curr).forEach(adj => queue.push([adj,distance + 1]))
    }
    
    return -1
    
};

但是我想知道我是否也可以获得到达矩阵末尾所需的实际路径,所以我不想返回一个数字作为路径的长度,而是返回一个坐标数组的实际路径它在短路径上访问。

例如,如果输入网格为[[0,1,0]],则应返回[ [ 0,0 ],[ 0,1 ],[ 1,2 ],[ 2,2 ] ] 

但这比看起来更难。我尝试了几种方法,但似乎无法解决。有人可以试试吗?

解决方法

主要思想是使 visited 成为 Map 而不是 Set,并将前一个节点在到该访问节点的路径上的坐标注册为 value

为此,我发现比您早一步制作已访问标记更容易:当您单元格放入队列时进行标记,而不是在您将其从队列中拉出时进行标记.

到达目的地后,您可以将 Map 用作链表,然后沿着路径返回源节点。这样你就可以重建路径。然后只需要将其反转即可。

这是您的代码,其中带有注释标记的修改:

var shortestPathBinaryMatrix = function(grid) {
    if(grid[0][0] != 0) return []; // modify return type
    const queue = [[[0,0],1]];
    const dest = [grid.length - 1,grid[0].length - 1];
    const visited = new Map();
    visited.set([0,0].toString(),null); // Mark source as visited

    const getNextSteps = ([x,y]) => {
        const dirs = [[1,[-1,[0,1],-1],[1,-1]];
        const nextSteps = [];
        for(const [nx,ny] of dirs) {
            if(grid[x + nx]?.[y + ny] == 0) nextSteps.push([x + nx,y + ny]);
        }
        return nextSteps;
    }
    
    for (let [curr,distance] of queue) {
        // Move the visited check to the loop
        if (curr[0] === dest[0] && curr[1] === dest[1] && grid[dest[0]][dest[1]] == 0) {
            // Derive the path from the linked list we now have in the visited structure:
            let path = [];
            while (curr) {
                path.push(curr);
                curr = visited.get(curr.toString());
            }
            return path.reverse(); // Need to reverse to get from source to destination
        }
        for (let adj of getNextSteps(curr)) {
            // Visited-check moved here:
            if (visited.has(adj.toString())) continue; 
            // Mark with the coordinates of the previous node on the path:
            visited.set(adj.toString(),curr);
            queue.push([adj,distance + 1]);
        }
    }
    
    return []; // must modify this as well
};

// demo
let grid = [[0,1,0]];
let result = shortestPathBinaryMatrix(grid);
console.log(result);

无关:养成用分号结束语句的习惯是件好事。让您的代码依赖于 automatic semi-colon insertion algorithm 当然是可能的,但您不会是第一个陷入其中一个晦涩陷阱的人。