下面是一种算法的尝试,用于在具有无重力边缘的图中找到最短路径,其中一个附加约束:一组不能在路径中的节点。因此,它不是找到节点之间的绝对最短路径,而是找到不包含某些节点的最短路径。
Wordnode 是节点类, HashSet避免是必须避免的节点集。算法中唯一发挥作用的地方是检查是否将节点添加到队列中。如果中的内容避免(或者已经访问过它),请不要添加它。我相信这个检查的效果应该等同于暂时删除 avoidids 中的任何边进出节点,尽管使用HashSet我避免实际改变数据结构。
我认为算法正在运行,直到我通过向避免添加单词来设法获得更短的路径。例如,如果避免为空,那么对于 shortestPath(A,Z,{}),它可能会返回(A,B,E,C,F,L,D, Z),但在向中添加E和C以避免并调用 shortestPath(A,Z,{E,C})时,我得到(A,R,K,Z) ),这更短......
我使用的图表有数千个节点,但我检查过(A,B,E,C,F,L,D,Z)和(A, R,K,Z)是有效路径。问题是当避免为空时,当有明显存在的长度路径仅为4时,算法返回长度为8的路径。
这告诉我,我的算法(下面)不正确,或者我的图形数据结构存在问题。检查后者会更加困难,所以我想我会看到是否有人首先发现问题。
那么,当避免非空时比下面的算法为空时,你能否看到下面的算法找到更短路径的原因?
注意:"这"是原点,而目的地(" dest")是一个参数。
由于
public LinkedList<String> shortestPath(Wordnode dest, int limit, HashSet<Wordnode> avoids)
{
HashSet<Wordnode> visited = new HashSet<>();
HashMap<Wordnode, Wordnode> previous = new HashMap<>();
LinkedList<Wordnode> q = new LinkedList<Wordnode>();
previous.put(this, null);
q.add(this);
Wordnode curr = null;
boolean found = false;
while(!q.isEmpty() && !found)
{
curr = q.removeLast();
visited.add(curr);
if(curr == dest)
found = true;
else
{
for(Wordnode n: curr.neighbors)
{
if(!visited.contains(n) && !avoids.contains(n))
{
q.addFirst(n);
previous.put(n, curr);
}
}
}
}
if(!found)
return null;
LinkedList<String> ret = new LinkedList<>();
while(curr != null)
{
ret.addFirst(curr.word);
curr = previous.get(curr);
}
return ret;
}
答案 0 :(得分:1)
您的BFS是正确的。问题是如何编写找到的路径。 BFS中的最短路径表示&#34;远离源到目的地的级别的数量&#34;。但是,您正在计算从源到目的地的路上查找的唯一节点数。
考虑每个相互连接的3个节点的图表:
B
/
A |
\
C
路径A-C长1级。您的实现可以给出路径长度为2,因为节点可以作为A-B然后C访问。顺序将取决于您的输入数据。 所以你需要计算水平。
答案 1 :(得分:1)
我认为您的问题是如何使用previous
地图构建边缘列表。在排队节点时存储最后看到的边缘,但此边缘可能不在最短路径上。
从队列中提取dest
时检查previous
,但dest
节点dest
中存储的边缘可能不再是到达avoids
的边缘{1}}将其添加到队列中。
当您提供previous
个节点时,您会跳过更新avoids
边缘的过程,这样可能会以更短的路径结束 - 不是{{1}是否已指定,而是avoids
是否包含可能“损坏”边列表的较长路径上的节点。
答案 2 :(得分:0)
我在这里添加了一个答案,因为前面没有一个表明正确的错误。
问题在于节点被标记为已访问:在原始节点从队列中弹出节点时完成,这意味着在给定节点到达队列顶部之前,它可能会被添加几次,因此改变路径建设。
你必须在排队时标记你的节点,所以在行队列之后.addFirst(n)只需添加visited.add(n)。