简体   繁体   English

广度优先搜索实现陷入无限循环

[英]Breadth first search implementation stuck in infinite loop

I'm trying to implement a breadth first search to solve a maze, but I keep getting stuck in an infinite loop and I have no idea why this is happening.我正在尝试实施广度优先搜索来解决迷宫问题,但我一直陷入无限循环,我不知道为什么会这样。 Rows and columns are the number of rows and columns for my maze/grid.行和列是我的迷宫/网格的行数和列数。 I am starting from the top left cell, and checking to see if a neighbour is within the grid before adding them to the queue.我从左上角的单元格开始,检查邻居是否在网格内,然后再将它们添加到队列中。

export function elementInGrid<T>(neighbour: number[], rows: number, columns: number): boolean{
    const [i, j] = neighbour;
    return (0 <= i && i <= rows - 1) && (0 <= j && j <= columns - 1);
}

function arrayEquality<T>(a: T[], b: T[]): boolean{
    return a.sort().toString() === b.sort().toString()
}

export const breadthFirstSearch = (rows: number, columns: number) => {
    const startPoint = [0, 0];
    const endPoint = [rows - 1, columns - 1];
    const queue = [startPoint];
    while (queue.length > 0){
        const [i, j] = queue.shift()!;
        if (arrayEquality([i, j], endPoint)){
            break
        }
        const neighbours = [[i - 1, j], [i, j + 1], [i + 1, j], [i, j - 1]]
        neighbours.forEach(neighbour => {
            if (elementInGrid(neighbour, rows, columns)){
                queue.push(neighbour);
            }
        })     
    }
}

Your algorithm assumes there are no cycles in the graph, which doesn't seem like it'd be the case given your neighbor list.您的算法假设图中没有循环,而给定您的邻居列表似乎并非如此。 The algorithm moves from cell 0, 0 to 0, 1 which has 0, 0 as a neighbor and gets enqueued.该算法从单元格0, 0移动到以 0 0, 0作为邻居的单元格 0 0, 1并入队。 Then, 0, 0 has 0, 1 as a neighbor which has 0, 0 as a neighbor....然后, 0, 00, 1作为邻居,而0, 0作为邻居......

It's unusual that you're ignoring coordinate ordering when checking whether you reached the destination but that's unrelated to the infinite loop ( arrayEquality is misleadingly named--it only checks primitives reliably due to stringification and ignores ordering).在检查是否到达目的地时忽略坐标排序是不寻常的,但这与无限循环无关( arrayEquality的名称具有误导性——由于字符串化,它只可靠地检查基元并忽略排序)。

From a design standpoint, I wouldn't hardcode the start and endpoints into the grid.从设计的角度来看,我不会将起点和终点硬编码到网格中。 I'm not sure it makes much sense to have every cell connected in the grid, either, so likely you'd want to pass in a graph with walls (ie neighbors are specific to each cell, assuming this is really a maze).我也不确定将每个单元格连接到网格中是否有意义,因此您很可能希望传入带有墙壁的图表(即邻居特定于每个单元格,假设这确实是一个迷宫)。

Also, the algorithm does nothing when it finds a path through the grid.此外,该算法在找到通过网格的路径时什么也不做。 I've taken the liberty to adjust a few of these things to make the example a bit more purposeful, omitting the maze neighboring.我冒昧地调整了其中的一些内容,以使示例更有目的性,省略了附近的迷宫。 I'm also using JS so you can run it in a stack snippet.我也在使用 JS,所以你可以在堆栈片段中运行它。

 const inGrid = (x, y, rows, cols) => x >= 0 && y >= 0 && x < cols && y < rows; const shortestPath = (rows, cols, src, dst) => { const queue = [[src, []]]; for (const visited = new Set(); queue.length;) { const [[x, y], path] = queue.shift(); const key = "" + [x, y]; if (visited.has(key)) continue; visited.add(key); path.push([x, y]); if (key === "" + dst) return path; [[x-1, y], [x, y+1], [x+1, y], [x, y-1]].forEach(e => { if (inGrid(...e, rows, cols)) { queue.push([e, path.slice()]); } }); } }; console.log(JSON.stringify(shortestPath(5, 5, [0, 0], [4, 4])));

Here's an example on a rather contrived maze with walls:这是一个相当人为的带有墙壁的迷宫的示例:

 const shortestPath = (maze, src, dst) => { const queue = [[src, []]]; for (const visited = new Set(); queue.length;) { const [[x, y], path] = queue.shift(); const key = "" + [x, y]; if (visited.has(key)) continue; visited.add(key); path.push([x, y]); if (key === "" + dst) return path; queue.push(...maze[key].map(e => [e, path.slice()])); } }; const maze = { "0,0": [[0, 1], [1, 0]], "0,1": [[0, 0], [0, 2]], "0,2": [[0, 1], [1, 2]], "1,0": [[0, 0], [1, 1]], "1,1": [[1, 0]], "1,2": [[0, 2]] }; /*.--------. | | --. | | | `--------` */ console.log(JSON.stringify(shortestPath(maze, [0, 0], [1, 2])));

This BFS algo goes into an infinite loop because the visited cells list are not stored anywhere.这个 BFS 算法进入一个无限循环,因为访问的单元列表没有存储在任何地方。 So, the algo has no clue if a cell has been explored earlier or not, which is the reason why same cells of the maze are getting visited repeatedly.因此,算法不知道一个单元是否已经被更早地探索过,这就是为什么重复访问迷宫的相同单元的原因。
To avoid such scenario we can have the maze itself in the function and each time we visit a cell we have to mark it as visited in the maze, so that next time if we encounter it in the loop it's not picked again and thus it will help us avoid the infinite loop.为了避免这种情况,我们可以将迷宫本身放在 function 中,每次我们访问一个单元格时,我们都必须将其标记为已在迷宫中访问过,这样下次如果我们在循环中遇到它就不会再次被选中,因此它将帮助我们避免无限循环。 We will create a maze with all elements set to 0(marks not visited) and then when we visit any cell, we are gonna set that cell value to 1. So, the next time we will ensure that we only pick those cells which are valid and not visited earlier.我们将创建一个所有元素设置为 0(未访问标记)的迷宫,然后当我们访问任何单元格时,我们将该单元格值设置为 1。因此,下一次我们将确保我们只选择那些单元格有效且之前未访问过。 The following is the correct code:以下是正确的代码:


    function elementInGrid(neighbour: number[], rows: number, columns: number): boolean{
        const [i, j] = neighbour;
        return (0 (a: T[], b: T[]): boolean{
        return a.sort().toString() === b.sort().toString()
    }

    const breadthFirstSearch = (rows: number, columns: number, maze: number[][]) => {
        const startPoint = [0, 0];
        const endPoint = [rows - 1, columns - 1];
        const queue = [startPoint];
        while (queue.length > 0){
            const [i, j] = queue.shift()!;
            maze[i][j] = 1
            if (arrayEquality([i, j], endPoint)){
                console.log("Reached target")
                break
            }
            const neighbours = [[i - 1, j], [i, j + 1], [i + 1, j], [i, j - 1]]
            neighbours.forEach(neighbour => {
                if (elementInGrid(neighbour, rows, columns) && (maze[neighbour[0]][neighbour[1]] == 0)){
                    console.log(neighbour)
                    queue.push(neighbour);
                    maze[neighbour[0]][neighbour[1]] = 1
                }
            })     
        }
    }
    var maze = [[0,0,0],[0,0,0],[0,0,0]]
    breadthFirstSearch(3,3,maze)

Hope that helps!!希望有帮助!!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM