java - 在查找最短路径时,广度优先搜索如何工作?
我做了一些研究,我似乎错过了这个算法的一小部分。 我理解广度优先搜索是如何工作的,但我不明白它究竟是如何让我走向一条特定的路径,而不是只告诉我每个节点可以去哪里。 我想解释我困惑的最简单方法是提供一个例子:
例如,让我们说我有一个这样的图:
我的目标是从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?
另外,请注意我实际上并没有使用树,因此没有" parent" 节点,只有孩子。
6个解决方案
67 votes
从技术上讲,广度优先搜索(BFS)本身并不能让您找到最短路径,因为BFS没有寻找最短路径:BFS描述了搜索图形的策略,但它没有说您必须搜索 什么特别的。
Dijkstra的算法适应BFS,让您找到单源最短路径。
为了检索从原点到节点的最短路径,您需要为图中的每个节点维护两个项目:当前最短距离,以及最短路径中的前一个节点。 最初所有距离都设置为无穷大,并且所有前驱都设置为空。 在您的示例中,将A的距离设置为零,然后继续使用BFS。 在每一步中,您检查是否可以改善后代的距离,即从原点到前一个的距离加上您正在探索的边的长度小于相关节点的当前最佳距离。 如果可以改善距离,请设置新的最短路径,并记住已获取该路径的前一个路径。 当BFS队列为空时,选择一个节点(在您的示例中,它是E)并将其前任遍历回原点。 这会给你最短的路径。
如果这听起来有点令人困惑,维基百科有一个很好的伪代码部分。
dasblinkenlight answered 2019-07-10T04:45:56Z
41 votes
如上所述,如果出现以下情况,BFS只能用于查找图表中的最短路径:
没有循环
所有边缘具有相同的重量或没有重量。
要找到最短路径,您所要做的就是从源开始并执行广度优先搜索,并在找到目标节点时停止。 您需要做的唯一额外事情是有一个数组previous [n],它将为每个访问过的节点存储上一个节点。 前一个源可以为null。
要打印路径,只需从源中直接遍历前一个[]数组,直到到达目标并打印节点。 DFS还可用于在类似条件下查找图中的最短路径。
但是,如果图形更复杂,包含加权边和循环,那么我们需要更复杂的BFS版本,即Dijkstra算法。
javaProgrammer answered 2019-07-10T04:46:59Z
19 votes
从这里的教程
"它具有非常有用的特性,即如果图中的所有边都未加权(或相同的权重),则第一次访问节点是从源节点到该节点的最短路径"
acheron55 answered 2019-07-10T04:47:32Z
8 votes
我浪费了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天尝试所有上述替代品,验证& 上面一遍又一遍地重新验证
他们不是问题。
(如果您没有发现上述问题,请尽量花时间寻找其他问题)。
以下评论的更多解释:
A
/ \
B C
/\ /\
D E F G
假设上面是你的图表
图表向下
对于A,邻接是B& C
对于B,邻接是D&Ë
对于C,邻接是F& G
比如说,起始节点是A.
当你到达A,to,B& C到B&的最短距离 A的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的所有路径都行进,并且因为将有许多路径我们丢弃除了最短路径之外的所有路径,然后继续前进到下一个节点J的最短路径
再次,如果从I到J有多条路径,我们只选择最短路径
例,
假设,
A - > 我有距离5
(STEP)假设,I - > 我们有多条距离为7& 7的路径。 8,因为7是最短的
我们采取A - > J为5(A-> I最短)+8(现在最短)= 13
所以A-> J现在是13
我们现在在上面重复(STEP)J - > K等等,直到我们到达Z.
阅读这部分,2或3次,并在纸上画画,你一定会得到我说的,祝你好运
Manohar Reddy Poreddy answered 2019-07-10T04:52:08Z
1 votes
根据acheron55的答案我在这里发布了一个可能的实现。
以下是它的简短摘要:
您所要做的就是跟踪到达目标的路径。一种简单的方法是将用于到达节点的整个路径推入Queue,而不是节点本身。
这样做的好处是,当达到目标时,队列将保持用于到达目标的路径。
这也适用于循环图,其中节点可以具有多个父节点。
c0der answered 2019-07-10T04:53:06Z
-4 votes
以下解决方案适用于所有测试用例。
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> adjacencyList = new HashMap>();
for (int j = 0; j < totalEdges; j++)
{
int src = sc.nextInt();
int dest = sc.nextInt();
if (adjacencyList.get(src) == null)
{
List neighbours = new ArrayList();
neighbours.add(dest);
adjacencyList.put(src, neighbours);
} else
{
List neighbours = adjacencyList.get(src);
neighbours.add(dest);
adjacencyList.put(src, neighbours);
}
if (adjacencyList.get(dest) == null)
{
List neighbours = new ArrayList();
neighbours.add(src);
adjacencyList.put(dest, neighbours);
} else
{
List neighbours = adjacencyList.get(dest);
neighbours.add(src);
adjacencyList.put(dest, neighbours);
}
}
int start = sc.nextInt();
Queue queue = new LinkedList<>();
queue.add(start);
int[] costs = new int[totalNodes + 1];
Arrays.fill(costs, 0);
costs[start] = 0;
Map visited = new HashMap();
while (!queue.isEmpty())
{
int node = queue.remove();
if(visited.get(node +"") != null)
{
continue;
}
visited.put(node + "", 1);
int nodeCost = costs[node];
List 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();
}
}
}
user3819236 answered 2019-07-10T04:53:31Z