首页 文章

在寻找最短路径时,广度优先搜索如何工作?

提问于
浏览
98

我做了一些研究,我似乎错过了这个算法的一小部分 . 我理解广度优先搜索是如何工作的,但我不明白它究竟是如何让我到达一条特定的路径,而不是只告诉我每个节点可以去哪里 . 我想解释我困惑的最简单方法是提供一个例子:

例如,假设我有一个这样的图形:

enter image description here

我的目标是从A到E(所有边缘都没有加权) .

我从A开始,因为那是我的起源 . 我排队A,然后立即将A排队并探索它 . 这产生B和D,因为A连接到B和D.因此我将B和D排队 .

我将B排队并探索它,并发现它导致A(已经探索过)和C,所以我排队C.然后我将D排队,并发现它导致E,我的目标 . 然后我将C出列,并发现它也导致E,我的目标 .

我从逻辑上知道最快的路径是A-> D-> E,但我不确定广度优先搜索有多精确 - 我应该如何记录路径,这样当我完成时,我可以分析结果并查看最短的路径是A-> D-> E?

另外,请注意我实际上并没有使用树,因此没有“父”节点,只有子节点 .

6 回答

  • 17

    从技术上讲,广度优先搜索(BFS)本身并不能让您找到最短路径,因为BFS没有寻找最短路径:BFS描述了搜索图形的策略,但它没有说您必须搜索什么特别的 .

    Dijkstra's algorithm适应BFS,让您找到单源最短路径 .

    为了检索从原点到节点的最短路径,您需要为图中的每个节点维护两个项目:当前最短距离,以及最短路径中的前一个节点 . 最初所有距离都设置为无穷大,并且所有前驱都设置为空 . 在您的示例中,将A的距离设置为零,然后继续使用BFS . 在每一步中,您检查是否可以改善后代的距离,即从原点到前一个的距离加上您正在探索的边的长度小于相关节点的当前最佳距离 . 如果可以改善距离,请设置新的最短路径,并记住已获取该路径的前一个路径 . 当BFS队列为空时,选择一个节点(在您的示例中,它是E)并将其前任遍历回原点 . 这会给你最短的路径 .

    如果这听起来有点令人困惑,维基百科就这个主题有一个很好的pseudocode section .

  • -2

    如上所述,如果出现以下情况,BFS可用于查找图中的最短路径:

    • 没有循环

    • 所有边缘重量相同或没有重量 .

    要找到最短路径,您只需从源开始并执行广度优先搜索,并在找到目标节点时停止 . 您需要做的唯一事情是有一个数组previous [n],它将存储每个访问过的节点的前一个节点 . 前一个源可以为null .

    要打印路径,请从源到源的前一个[]数组进行简单循环,直到到达目标并打印节点 . DFS还可用于在类似条件下查找图中的最短路径 .

    但是,如果图形更复杂,包含加权边和循环,那么我们需要更复杂的BFS版本,即Dijkstra算法 .

  • 39

    tutorial here

    “它具有非常有用的特性,如果图中的所有边都未加权(或相同的权重),则第一次访问节点是从源节点到该节点的最短路径”

  • 1

    我浪费了3天
    最终解决了一个图形问题
    用于
    找到最短的距离
    使用BFS

    想分享经验 .

    When the (undirected for me) graph has
    fixed distance (1, 6, etc.) for edges
    
    #1
    We can use BFS to find shortest path simply by traversing it
    then, if required, multiply with fixed distance (1, 6, etc.)
    
    #2
    As noted above
    with BFS
    the very 1st time an adjacent node is reached, it is shortest path
    
    #3
    It does not matter what queue you use
       deque/queue(c++) or
       your own queue implementation (in c language)
       A circular queue is unnecessary
    
    #4
    Number of elements required for queue is N+1 at most, which I used
    (dint check if N works)
    here, N is V, number of vertices.
    
    #5
    Wikipedia BFS will work, and is sufficient.
        https://en.wikipedia.org/wiki/Breadth-first_search#Pseudocode
    

    我已经失去了3天尝试所有上述选择,一次又一次地验证和重新验证
    他们不是问题 .
    (尝试花时间寻找其他问题,如果你发现上述任何问题5) .


    More explanation 来自以下评论:

    A
         /  \
      B       C
     /\       /\
    D  E     F  G
    

    假设上面是你的图表
    图表向下
    对于A,邻接是B&C
    对于B,邻接是D&E
    对于C,邻接是F&G

    比如说,起始节点是A.

    • 当你到达A,to,B&C时,从A到B&C的最短距离是1

    • 当到达D或E时,通过B,到A&D的最短距离是2(A-> B-> D)

    类似地,A-> E是2(A-> B-> E)

    另外,A-> F&A-> G是2

    所以,现在不是节点之间的1个距离,如果它是6,那么只需将答案乘以6即可
    例,
    如果每个之间的距离是1,那么A-> E是2(A-> B-> E = 1 1)
    如果每个之间的距离是6,那么A-> E是12(A-> B-> E = 6 6)

    是的,bfs可能采取任何路径
    但我们正在计算所有路径

    如果你必须从A到Z,那么我们将从A到中间I的所有路径都行进,并且由于将有许多路径我们丢弃除了最短路径之外的所有路径,然后继续前进到下一个节点的最短路径Ĵ
    再次,如果从I到J有多条路径,我们只选择最短路径
    例,
    假设,
    A - >我有距离5
    (STEP)假设,I - > J我们有多条距离为7和8的路径,因为7是最短的
    我们取A - > J为5(A-> I shortest)8(现在最短)= 13
    所以A-> J现在是13
    我们现在重复上面(STEP)的J - > K等等,直到我们到达Z.

    阅读这部分,2或3次,并在纸上画画,你一定会得到我说的,祝你好运


  • 61

    基于acheron55 answer我发布了一个可能的实现here .
    以下是它的简要总结:

    您所要做的就是跟踪到达目标的路径 . 一种简单的方法是将 Queue 用于到达节点的整个路径,而不是节点本身 .
    这样做的好处是,当达到目标时,队列将保持用于到达目标的路径 .
    这也适用于循环图,其中节点可以具有多个父节点 .

  • 7

    以下解决方案适用于所有测试用例 .

    import java.io.*;
    import java.util.*;
    import java.text.*;
    import java.math.*;
    import java.util.regex.*;
    
    public class Solution {
    
       public static void main(String[] args)
            {
                Scanner sc = new Scanner(System.in);
    
                int testCases = sc.nextInt();
    
                for (int i = 0; i < testCases; i++)
                {
                    int totalNodes = sc.nextInt();
                    int totalEdges = sc.nextInt();
    
                    Map<Integer, List<Integer>> adjacencyList = new HashMap<Integer, List<Integer>>();
    
                    for (int j = 0; j < totalEdges; j++)
                    {
                        int src = sc.nextInt();
                        int dest = sc.nextInt();
    
                        if (adjacencyList.get(src) == null)
                        {
                            List<Integer> neighbours = new ArrayList<Integer>();
                            neighbours.add(dest);
                            adjacencyList.put(src, neighbours);
                        } else
                        {
                            List<Integer> neighbours = adjacencyList.get(src);
                            neighbours.add(dest);
                            adjacencyList.put(src, neighbours);
                        }
    
    
                        if (adjacencyList.get(dest) == null)
                        {
                            List<Integer> neighbours = new ArrayList<Integer>();
                            neighbours.add(src);
                            adjacencyList.put(dest, neighbours);
                        } else
                        {
                            List<Integer> neighbours = adjacencyList.get(dest);
                            neighbours.add(src);
                            adjacencyList.put(dest, neighbours);
                        }
                    }
    
                    int start = sc.nextInt();
    
                    Queue<Integer> queue = new LinkedList<>();
    
                    queue.add(start);
    
                    int[] costs = new int[totalNodes + 1];
    
                    Arrays.fill(costs, 0);
    
                    costs[start] = 0;
    
                    Map<String, Integer> visited = new HashMap<String, Integer>();
    
                    while (!queue.isEmpty())
                    {
                        int node = queue.remove();
    
                        if(visited.get(node +"") != null)
                        {
                            continue;
                        }
    
                        visited.put(node + "", 1);
    
                        int nodeCost = costs[node];
    
                        List<Integer> children = adjacencyList.get(node);
    
                        if (children != null)
                        {
                            for (Integer child : children)
                            {
                                int total = nodeCost + 6;
                                String key = child + "";
    
                                if (visited.get(key) == null)
                                {
                                    queue.add(child);
    
                                    if (costs[child] == 0)
                                    {
                                        costs[child] = total;
                                    } else if (costs[child] > total)
                                    {
                                        costs[child] = total;
                                    }
                                }
                            }
                        }
                    }
    
                    for (int k = 1; k <= totalNodes; k++)
                    {
                        if (k == start)
                        {
                            continue;
                        }
    
                        System.out.print(costs[k] == 0 ? -1 : costs[k]);
                        System.out.print(" ");
                    }
                    System.out.println();
                }
            }
    }
    

相关问题