当前位置: 首页 > 知识库问答 >
问题:

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

贺宜修
2023-03-14

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

共有3个答案

须峰
2023-03-14

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

你的分析是这样的:

  • 设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)。

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

V·(c1c2E/V)

=c1V c2E

=Θ(V E)

这里发生的事情是,这些可爱的低阶项,大O很方便地让我们忽略,实际上在这里很重要,所以我们不能轻易丢弃它们。所以至少在数学上是这样的。

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

太叔高义
2023-03-14

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

邻接表

V     E
v0:{v1,v2} 
v1:{v3}
v2:{v3}
v3:{}

如何一步一步地操作BFS

第一步:

邻接列表:

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| + |E0| + |E1| + |E2| +|E3| == |V|+|E|
 4  +  2   +  1   +   1  + 0   ==  4 + 4
                           8   ==  8

假设采用邻接列表表示,V是顶点数,E是边数。

每个顶点最多排队一次。

扫描所有相邻顶点需要O(|E |)时间,因为邻接列表的长度之和是|E |

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

赫连越
2023-03-14

我希望这对理解广度优先搜索(又名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

我试图简化代码和复杂度计算,但如果你有任何问题,请告诉我。

 类似资料:
  • 二叉树上广度优先搜索的空间复杂度是多少?因为它一次只存储一个级别,我不认为它会是O(n)。

  • 主要内容:深度优先搜索(简称“深搜”或DFS),广度优先搜索,总结前边介绍了有关图的 4 种存储方式,本节介绍如何对存储的图中的顶点进行遍历。常用的遍历方式有两种: 深度优先搜索和 广度优先搜索。 深度优先搜索(简称“深搜”或DFS) 图 1 无向图 深度优先搜索的过程类似于树的先序遍历,首先从例子中体会深度优先搜索。例如图 1 是一个无向图,采用深度优先算法遍历这个图的过程为: 首先任意找一个未被遍历过的顶点,例如从 V1 开始,由于 V1 率先访问过了,所以

  • 我必须为一个算法开发伪代码,该算法计算给定顶点V和边E的图G=(V,E)中连通分量的数量。 我知道我可以使用深度优先搜索或广度优先搜索来计算连接组件的数量。 但是,我想用最高效的算法来解决这个问题,但是我不确定每个算法的复杂度。 下面是一个用伪代码形式写DFS的尝试。 下面尝试以伪代码形式编写 BFS。 我的讲师说BFS与DFS具有相同的复杂性。 然而,当我搜索深度优先搜索的复杂度时,它是O(V^

  • 我已经查看了其他各种StackOverflow答案,它们都与我的讲师在幻灯片中写的不同。 深度优先搜索的时间复杂度为O(b^m),其中b是搜索树的最大分支因子,m是状态空间的最大深度。如果m比d大得多,这很糟糕,但如果搜索树“浓密”,则可能比广度优先搜索快得多。 他接着说。。 空间复杂度为O(bm),即动作序列长度的空间线性!只需要存储从根到叶节点的单个路径,以及路径上每个节点的剩余未扩展兄弟节点

  • 在继续使用其他图算法之前,让我们分析广度优先搜索算法的运行时性能。首先要观察的是,对于图中的每个顶点 $$|V|$$ 最多执行一次 while 循环。因为一个顶点必须是白色,才能被检查和添加到队列。这给出了用于 while 循环的 $$O(V)$$。嵌套在 while 内部的 for 循环对于图中的每个边执行最多一次,$$|E|$$。原因是每个顶点最多被出列一次,并且仅当节点 u 出队时,我们才检

  • 我正在研究BFS算法,我有一个关于如何将相邻节点插入队列的问题。 例如,假设我们正在处理一个无向图,我们希望执行BFS来输出图的内容,那么我们如何知道在从队列中取出一个初始节点后,相邻节点以什么顺序插入到队列中呢?还有,有没有办法修改邻居节点插入队列的方式? 任何帮助都将不胜感激,谢谢。