【问题标题】:Difficulty implementing BFS for traversal of graph难以实现图遍历的 BFS
【发布时间】:2015-07-07 13:39:59
【问题描述】:

我有一个邻接矩阵 adj,定义如下:

0 0 0 1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 1 0 0 0 
1 0 0 0 1 0 1 0 0 
0 0 0 1 0 1 0 0 0 
0 0 1 0 1 0 0 0 1 
0 0 0 1 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 
0 0 0 0 0 1 0 0 0 

我正在绞尽脑汁设计一个 BFS 算法来遍历给定起始位置和结束位置的图。

我的最佳尝试确实产生了一系列动作,但不是最短的。

boolean[] pass = new boolean[9]; //if a move has been done, don't redo it
int s = 0; //starting position
int e = 2; //ending position
int[][] matrix; //the adjacency matrix previous defined

public List<Integer> BFS(int[][] matrix, int s) {
    List<Integer> paths = new LinkedList();
    List<Integer> shortest = new LinkedList();
    pass[s] = true; //starting position has been indexed
    paths.add(0,s); //insert part of path to front of list
    while (paths.isEmpty() == false) {
        int element = paths.get(0); //peek at first element
        shortest.add(element); //add it to shortest path
        int node = paths.remove(0); //remove from path
        if (element == e) { //if we've reached the end
            return shortest; //hopefully found shortest path
        } else {
            for (int i = 0; i < 9; i++) {
                //if adjacent element hasn't been indexed
                if (pass[i] == false && matrix[node][i] == 1) {
                    pass[i] = true;
                    paths.add(0,i);
                }
            }
        }
    }
    return null;
}

打印返回的列表产生:

[0, 3, 6, 4, 5, 8, 2]

实际结果应该是什么时候:

[0, 3, 4, 5, 2]

在我看来,它所走的道路是这样的:

[0, 3, 6, backtrack to 3, 4, 5, 8, backtrack to 5, 2]

我的算法有什么问题?如何找到给定起点和终点的最短路径?

Here 是一个用于说明的 IDE。

【问题讨论】:

  • 你的 BFS 遍历没问题,但是你没有正确提取最短路径。您正在将您处理的 每个 节点添加到最短路径。

标签: java algorithm search graph-theory adjacency-matrix


【解决方案1】:

您实现中的shortest 数组不是您想的那样。它只是找到的所有顶点的列表,而不是最短路径,因为(如您所见)BFS 有时必须遵循不通向目标顶点的路径。

您需要为每个顶点 v 存储其“父节点”——您从该顶点到 v

       if (pass[i] == false && matrix[node][i] == 1) {
            pass[i] = true;
            parent[i] = node;
            paths.add(0,i);
       }

然后在主 BFS 循环之后,您使用存储在 parent 中的链接从目标顶点返回源:

// pseudocode
cur = e;
while cur != s
    answer.add_at_end(cur)
    cur = parent[cur];

【讨论】:

    【解决方案2】:

    您没有正确提取最短路径。您正在将您处理的 每个 节点添加到最短路径。

    如果你想使用 BFS 来找到最短路径,你必须通过存储“后边”来跟踪到每个中间节点的最短路径,只有最后你才能将它们放在一起找到最短路径。

    对于您添加到队列中的每个节点,将使您到达此节点的节点存储为“后边缘”:

    if (pass[i] == false && matrix[node][i] == 1) {
        pass[i] = true;
        paths.add(0,i);
    
        // Add this:
        back[i] = node;
    }
    

    然后,一旦您的遍历完成,您可以使用这些引用通过从末端节点向后走那些“后边缘”来找到完整路径。它将引导您(向后)到最短路径上的起始节点:

    int node = e;
    while (node != s) {
        shortest.add(0, node);
        node = back[node];
    }
    shortest.add(0, s);
    

    更新

    感谢您的评论,我刚刚意识到您的代码存在其他问题。您正在将新节点添加到列表的前面并从前面处理它们。所以你实际上有一个堆栈,而不是一个队列。这使您的算法有效地成为深度优先搜索 (DFS) 而不是广度优先搜索 (BFS)。 DFS 将为您提供从se 的正确路径,但不一定是最短路径。

    为了将您的列表paths(最好命名为queue,顺便说一句。)作为队列而不是堆栈,您必须从相反的两端读取和写入,例如添加到后面(而不是前面)更改列表添加行

    paths.add(0, i);
    

    paths.add(i);
    

    【讨论】:

    • 我跟随,但采用这种方法会提供输出[3, 4, 5, 2, 3, 4, 5, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0]
    • @gator:我猜你把“旧”shortest 列表和新列表混在一起了。
    • @gator: 见goo.gl/IydTAe 你可以在线执行并检查它是否返回正确的结果[0, 3, 4, 5, 2]
    • 这适用于给定的邻接矩阵,但不适用于所有邻接矩阵。参见ideone.com/fork/PTVtNj,当最短路径为[0, 6, 7, 13, 14, 15, 16, 17, 11, 5](11 个元素)时,打印列表为[0, 6, 7, 13, 19, 25, 26, 27, 28, 29, 23, 17, 11, 5](14 个元素)。
    • @gator:我明白了。刚刚发现您的代码的另一个问题并更新了答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-17
    • 1970-01-01
    • 2020-08-03
    • 1970-01-01
    • 2013-01-06
    相关资源
    最近更新 更多