修改BFS时间复杂度

时间:2017-11-07 17:26:31

标签: algorithm graph-theory graph-algorithm breadth-first-search

所以当队列实现时,BFS的复杂度为O(| V | + | E |):

ENQUEUE(Q,source_vertex)
while Q != NULL
   u=DEQUEUE(Q)
   for each v in AdjacencyList[u]
     if v not yet visited
        Make v visited
        ENQUEUE(Q,v)

如果我修改代码,将u的邻接列表中的所有顶点添加到队列中,如下所示:

ENQUEUE(Q,source_vertex)
while Q != NULL
   u=DEQUEUE(Q)
   for each v in AdjacencyList[u]
      if v not finalized            
         ENQUEUE(Q,v)
   make u finalized

运行时间是否仍为O(| V | + | E |)?

提前致谢。

3 个答案:

答案 0 :(得分:3)

想象一下你有一个n个节点的集团(让我们将它们编号为1,2,...,n并假设邻接列表按此顺序存储它们)并从节点1开始运行修改后的算法。节点1将入队节点2,3,...,n用于总共Θ(n)的工作。队列现在看起来像这样:

  

2,3,4,...,n

当您处理节点2时,它会查看其所有边缘的Θ(n)更多工作,然后将队列中的节点3,4,5,...,n排队。我们的队列现在看起来像这样:

  

3,4,5,...,n,3,4,5,...,n

我们现在处理节点3,它查看其所有边缘的Θ(n)工作,然后将节点的4,5,6,...,n排队,所以我们的队列如下所示:

  

4,5,6,...,n,3,4,5,...,n,4,5,6,...,n

这里的模式是我们最终将图中每个节点的许多副本排入队列。实际上,随着时间的推移,我们最终会在队列中总共有Θ(n 2 )总节点,并且我们每个节点执行Θ(n)工作。这意味着该图完成的总工作量为Θ(n 3 ),超过了原始BFS实现的O(m + n)时间限制。

因此,这个新实现可能比常规BFS渐近地慢。

答案 1 :(得分:1)

让我们更改你的算法以向队列添加边缘(因为这是隐含的你正在做的事情 - 除了你只是在将它添加到队列而不是整个边缘时查看相反的顶点):

ENQUEUE( Q,(NULL->source_vertex) )            # start with a dummy edge
WHILE Q != NULL
  (s->u)=DEQUEUE(Q)
  for each (u->v) in AdjacencyList[u]   
    if v not finalized
      ENQUEUE(Q,(u->v))
  make u finalized

每个边缘将被视为(u->v)(v->u)两次排队。当访问边u的第一个顶点时,每个相邻边将被放入队列,然后u完成。访问v时,将考虑边(v->u),但由于u已经完成,因此不会添加到队列中;所以每个边缘只会排队一次(在一个方向但不在另一个方向)。

该算法的问题在于它不会检查它要处理的顶点是否已经完成,并将为队列中的每个边缘重新处理它,再次迭代所有相邻边缘,使您的算法成为O(|V||E|) (而不是O(|V| + |E|))。

一个简单的解决方法是:

ENQUEUE( Q,(NULL->source_vertex) )            # start with a dummy edge
WHILE Q != NULL
  (s->u)=DEQUEUE(Q)
  if u not finalized
    for each (u->v) in AdjacencyList[u]
      if v not finalized
        ENQUEUE(Q,(u->v))
    make u finalized

此外,算法的两个版本都将从一个顶点开始,然后处理同一个连接组件中的每个边缘 - 但是它们只会在连接的图形上执行完整的BFS。如果您有多个断开连接的组件,则需要使用:

for each source_vertex in Graph
  if source_vertex not visited/finalised
    call your_algorithm( source_vertex )

然后O(|V| + |E|)将会访问所有顶点,无论图表是否已连接。

答案 2 :(得分:0)

据我了解,运行时复杂性也是O(|V|+|E|)。第二个版本只是将 finalization 步骤(也可以称为访问)推迟到下一次迭代;递归调用的数量和顶点序列都没有改变。