Algorithm 广度优先搜索时间复杂度分析

Algorithm 广度优先搜索时间复杂度分析,algorithm,graph,time-complexity,breadth-first-search,Algorithm,Graph,Time Complexity,Breadth First Search,遍历顶点的每个相邻边的时间复杂度是,例如,O(N),其中N是相邻边的数量。因此,对于V顶点数,时间复杂度变为O(V*N)=O(E),其中E是图中的边总数。既然从队列中移除和添加顶点是O(1),为什么它会作为O(V+E)添加到BFS的总体时间复杂度中?执行O(1)操作L次,结果是O(L)复杂度。 V * (O(1) + O(Eaj) + O(1)) V + V * Eaj + V 2V + E(total number of edges in graph) V + E 因此,从队列中移除和添加顶

遍历顶点的每个相邻边的时间复杂度是,例如,
O(N)
,其中
N
是相邻边的数量。因此,对于
V
顶点数,时间复杂度变为
O(V*N)
=
O(E)
,其中
E
是图中的边总数。既然从队列中移除和添加顶点是
O(1)
,为什么它会作为
O(V+E)
添加到BFS的总体时间复杂度中?

执行
O(1)
操作
L
次,结果是
O(L)
复杂度。
V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
因此,从队列中移除和添加顶点是O(1),但当您对
V
顶点执行此操作时,您会得到
O(V)
复杂性。
因此,
O(V)+O(E)=O(V+E)

考虑下图,我们可以看到时间复杂度是O(| V |+| E |),而不是O(V*E)

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
邻接表

V     E
v0:{v1,v2} 
v1:{v3}
v2:{v3}
v3:{}
V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
逐步操作BFS的工作原理

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
步骤1:

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
邻接列表:

V     E
v0: {v1,v2} mark, enqueue v0
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2} dequeue v0;mark, enqueue v1,v2
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3} dequeue v1; mark,enqueue v3
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3} dequeue v2, check its adjacency list (v3 already marked)
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3}
v3: {} dequeue v3; check its adjacency list
V     E
v0: {v1,v2} |E0|=2
v1: {v3}    |E1|=1
v2: {v3}    |E2|=1
v3: {}      |E3|=0
V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
步骤2:

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
邻接列表:

V     E
v0: {v1,v2} mark, enqueue v0
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2} dequeue v0;mark, enqueue v1,v2
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3} dequeue v1; mark,enqueue v3
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3} dequeue v2, check its adjacency list (v3 already marked)
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3}
v3: {} dequeue v3; check its adjacency list
V     E
v0: {v1,v2} |E0|=2
v1: {v3}    |E1|=1
v2: {v3}    |E2|=1
v3: {}      |E3|=0
V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
步骤3:

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
邻接列表:

V     E
v0: {v1,v2} mark, enqueue v0
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2} dequeue v0;mark, enqueue v1,v2
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3} dequeue v1; mark,enqueue v3
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3} dequeue v2, check its adjacency list (v3 already marked)
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3}
v3: {} dequeue v3; check its adjacency list
V     E
v0: {v1,v2} |E0|=2
v1: {v3}    |E1|=1
v2: {v3}    |E2|=1
v3: {}      |E3|=0
V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
步骤4:

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
邻接列表:

V     E
v0: {v1,v2} mark, enqueue v0
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2} dequeue v0;mark, enqueue v1,v2
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3} dequeue v1; mark,enqueue v3
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3} dequeue v2, check its adjacency list (v3 already marked)
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3}
v3: {} dequeue v3; check its adjacency list
V     E
v0: {v1,v2} |E0|=2
v1: {v3}    |E1|=1
v2: {v3}    |E2|=1
v3: {}      |E3|=0
V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
步骤5:

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
邻接列表:

V     E
v0: {v1,v2} mark, enqueue v0
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2} dequeue v0;mark, enqueue v1,v2
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3} dequeue v1; mark,enqueue v3
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3} dequeue v2, check its adjacency list (v3 already marked)
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3}
v3: {} dequeue v3; check its adjacency list
V     E
v0: {v1,v2} |E0|=2
v1: {v3}    |E1|=1
v2: {v3}    |E2|=1
v3: {}      |E3|=0
V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
步骤6:

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
邻接列表:

V     E
v0: {v1,v2} mark, enqueue v0
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2} dequeue v0;mark, enqueue v1,v2
v1: {v3}
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3} dequeue v1; mark,enqueue v3
v2: {v3}
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3} dequeue v2, check its adjacency list (v3 already marked)
v3: {}
V     E
v0: {v1,v2}
v1: {v3}
v2: {v3}
v3: {} dequeue v3; check its adjacency list
V     E
v0: {v1,v2} |E0|=2
v1: {v3}    |E1|=1
v2: {v3}    |E2|=1
v3: {}      |E3|=0
V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
步骤总数:

|V| + |E0| + |E1| + |E2| +|E3| == |V|+|E|
 4  +  2   +  1   +   1  + 0   ==  4 + 4
                           8   ==  8
V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
假设采用邻接列表表示,V为顶点数,E为边数

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
每个顶点最多排队一次

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
扫描所有相邻顶点需要O(| E |)时间,因为相邻列表的长度之和是| E |

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E

因此,BFS的时间复杂度为O(| V |+| E |)时间复杂度。

我希望这对理解广度优先搜索(也称BFS)的计算时间复杂度有困难的人有所帮助

Queue graphTraversal.add(firstVertex);

// This while loop will run V times, where V is total number of vertices in graph.
while(graphTraversal.isEmpty == false)

    currentVertex = graphTraversal.getVertex();

    // This while loop will run Eaj times, where Eaj is number of adjacent edges to current vertex.
    while(currentVertex.hasAdjacentVertices)
        graphTraversal.add(adjacentVertex);

    graphTraversal.remove(currentVertex);
V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
时间复杂度如下:

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E

我已经尝试过简化代码和复杂度计算,但是如果您有任何问题,请告诉我。

这里的其他答案很好地展示了BFS如何运行以及如何分析它。我想重温一下你最初的数学分析,具体地说,你的推理给出的估计值低于真实值

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
您的分析如下:

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
  • 设N为入射到每个节点的平均边数(N=E/V)
  • 因此,每个节点花费O(N)个时间在队列上执行操作
  • 由于有V个节点,所以总运行时间是O(V)·O(N)=O(V)·O(E/V)=O(E)
你很快就能得到正确的估计。问题是缺少的V项从何而来。这里的问题是,奇怪的是,你不能说O(V)·O(E/V)=O(E)

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
每个节点的平均功是O(E/V),这是完全正确的。这意味着,从上到下,完成的总功以E/V的倍数为界。如果我们考虑BFS实际在做什么,每个节点完成的功可能更像c1+c2E/V,因为每个节点都有一些基线工作量(设置循环、检查基本条件等),这就是c1项所解释的,加上与访问的边数成比例的工作量(E/V,乘以每条边完成的工作量)。如果我们把这个乘以V,我们就得到了

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
V·(c1+c2E/V)

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
=c1V+c2E

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
=Θ(V+E)

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E
这里发生的事情是,这些可爱的低阶项,大O很方便地让我们忽略,实际上在这里很重要,所以我们不能轻易地丢弃它们。所以这至少在数学上是正在发生的事情

V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E

这里实际发生的是,无论图中有多少条边,对于每个节点,都必须独立于这些边进行一些基线工作量。这就是运行核心if语句、设置局部变量等的设置。

我只想在上面的答案中补充一点,如果我们使用邻接矩阵而不是邻接列表,时间复杂度将是O(V^2),因为我们必须为每个顶点遍历一整行,以检查哪些节点是相邻的。

您是说总复杂度应该是O(V*N)=O(E)。假设任何一对顶点之间没有边,即所有顶点v的Adj[v]为空。在这种情况下,BFS需要恒定的时间吗?答案是否定的。这需要O(V)时间(更准确地说是θ(V))。即使Adj[v]为空,对每个顶点运行检查Adj[v]的行本身也需要一些恒定的时间。所以BFS的运行时间是O(V+E),意思是O(max(V,E))。

谢谢,很好的解释。这在两年后非常有用,谢谢!方程中的Eaj部分是否应该被包装为O(Eaj)?是的@Ajfigueroa有一点我们可以补充的是,“Eaj”max可以是“V-1”(在完全连通的图中是总顶点)和Min 0(在断开的图中是最小0),在这种情况下方程:
V*Eaj+2V
对于max=
2V+V(V-1)
O(V^2)
对于min
O(V)
.O(1)+O(Eaj)+O(1)不仅仅是O(Eaj)?应该注意,为了避免循环或多次访问顶点,应将访问的顶点标记为HI,欢迎使用堆栈溢出!请坐飞机。感谢您的回答,但是您是否可以添加一个关于代码如何解决问题的解释?有关如何设置答案格式的信息,请查看。
V * (O(1) + O(Eaj) + O(1))
V + V * Eaj + V
2V + E(total number of edges in graph)
V + E