这里有一个伪代码算法,试图添加注释来解释它的工作原理。
declare explored // a table of all the vertices that can keep track of one number (distance, initialized to -1) and a list of vertex references (origins, initialized to null)
to_explore = S // fifo queue of vertices to explore
while (to_explore not empty) {
pop vertex v from to_explore
current_distance = explored[v].distance
current_origins = explored[v].origins
for (vertex n, neighbor of v) {
if (explored[n].origins contains v)
continue // we just hit a loop and we're only interested in shortest distances
if (explored[n].distance == -1) { // first time we come here
explored[n].distance = current_distance+1
explored[n].origins = current_origins
push n to to_explore
continue
}
if (explored[n].distance != current_distance+1) {
continue // we are merging path from another node of S but different distance, cannot lead to any solution
}
// only case left is explored[n].distance == current_distance+1
// so we've already come here from other place(s) in S with the same distance
add / merge (without duplicates) origins to explored[n].origins
if (explored[n].origins = S) // maybe compare the size is enough?
return n // we found a solution
// if not , we just continue our exploration, no need to add to the queue since we've already been through here before
}
}
使用FIFO队列的想法是,我们将探索离集合S距离为1的所有内容,如果在那里找不到任何解决方案,则探索距离为2的所有内容...等等,因此我们将首先找到最短的距离。
我不完全确定复杂度,但我认为在最坏的情况下,我们只会探索每个顶点和每个边仅一次,因此复杂度为O(| E | + | V |)。 但在某些情况下,我们会多次访问相同的顶点。虽然这并不会增加要探索的路径太多,但我不确定是否应该在某个地方有一个因子| S |(但如果将其视为常数,则可以...)
希望我没有漏掉任何内容。显然,我没有测试过任何内容....:)
编辑(回复评论)
您的代码能否适用于此类图形?E =(a,b),(a,c),(a,d),(b,e),(c,e),(d,e),而我的F = {b,c,d}。假设您从a开始进行bfs。我怀疑,最终origins数组将只有{a},因此代码将返回None。- Guru Devanla
在这种情况下,以下是发生的情况:
to_explore is initialized to {b,c,d}
pop one from to_explore (b). to_explore becomes {c,d}
current_distance=0
current_origins={b}
handling 'a' as neighbor of b first
explored[a].distance=1
explored[a].origins={b}
to_explore becomes {c,d,a}
handling 'e' as next neighbor of b
explored[e].distance=1
explored[e].origins={b}
to_explore becomes {c,d,a,e}
pop one from to_explore (c). to_explore becomes {d,a,e}
current_distance=0
current_origins={c}
handling 'a' as neighbor of c first
explored[a].distance is already 1
explored[a].origins={b,c}
to_explore already contains a
handling 'e' as next neighbor of b
explored[e].distance is already 1
explored[e].origins={b,}
to_explore already contains e
pop one from to_explore (d)
current_distance=0
current_origins={d}
handling 'a' as neighbor of d first
explored[a].distance is already 1
explored[a].origins={b,c,d}
that matches F, found a as a solution.