Data structures 解释BFS和DFS的运行时

Data structures 解释BFS和DFS的运行时,data-structures,graph,time-complexity,depth-first-search,breadth-first-search,Data Structures,Graph,Time Complexity,Depth First Search,Breadth First Search,为什么BFS和DFS的运行时间是O(V+E),特别是当有一个节点具有指向可以从顶点到达的节点的定向边时,如下面站点中的本例所示 E是图中所有边的集合,如G={V,E}。所以,| E |是图中所有边的计数 仅此一点就足以让您相信,总体复杂度不可能是| V |乘以| E |,因为我们并没有迭代图中每个顶点的所有边 在邻接列表表示法中,对于每个顶点v,我们只遍历与其相邻的节点 |V |+| E |的| V |因子似乎来自完成的队列操作数 请注意,算法的复杂性取决于所使用的数据结构。 实际上,我们是在访

为什么BFS和DFS的运行时间是O(V+E),特别是当有一个节点具有指向可以从顶点到达的节点的定向边时,如下面站点中的本例所示


E是图中所有边的集合,如G={V,E}。所以,| E |是图中所有边的计数

仅此一点就足以让您相信,总体复杂度不可能是| V |乘以| E |,因为我们并没有迭代图中每个顶点的所有边

在邻接列表表示法中,对于每个顶点v,我们只遍历与其相邻的节点

|V |+| E |的| V |因子似乎来自完成的队列操作数

请注意,算法的复杂性取决于所使用的数据结构。 实际上,我们是在访问图的表示中存在的每一条信息,这就是为什么对于图的矩阵表示,复杂性变为V平方

引用科尔曼的话

“排队和退队的操作需要O(1)个时间,因此 用于队列操作的总时间为O(V)。因为邻接 每个顶点的列表仅在顶点出列时扫描,每个 邻接列表最多扫描一次。因为 在所有邻接列表中,Θ(E)是扫描所花费的总时间 邻接列表是O(E)。初始化的开销是O(V), 因此,BFS的总运行时间为O(V+E)。”


这个问题花费了我4个小时的时间,但最后,我想我有了一个简单的方法来理解这个问题,一开始我想说O(V*E)

总结一下你在Cormen发现的算法,这在你身上是一样的,你有这样的东西:

for (vi in V)
{
   Some O(1) instructions
   for ( e in Adj(vi) ) {
      Some O(1) instructions
   }
}
问题是这里执行了多少指令?这就是Sigma和(Adj(vi)),这个值的上界是2 | E |


开始时,我们会自动考虑将内循环和外循环的迭代次数相乘,但在这种情况下,内循环的总迭代次数是外循环的函数,因此不可能相乘。

您在| V |节点上迭代最多| V |次。因为我们在图中总共有一个| E |边的上界,所以我们最多检查| E |边。不同的顶点将有不同数量的相邻节点,因此我们不能只乘以| V |*| E |(这意味着对于每个顶点,我们遍历| E |边,这不是真的,| E |是所有节点上的边的总数),相反,我们检查V个节点,检查总共E条边。最后,我们有O(| V |+| E |)

对于DFS,这是类似的,我们循环遍历所有顶点邻接列表,如果未访问DFS(v),则调用DFS(v),这意味着我们产生了| v |时间步,加上访问相邻节点的时间(基本上,这些节点形成了一条边,我们总共有| E |边,因此是O(v+E)时间


每个边最多访问两次。有E个边。因此将有2*E个边访问操作。加上那些没有边的节点,或者换句话说,阶数为0的节点。最多可以有V个这样的节点。因此复杂性是,O(2*E+V)=O(E+V)

当您将图形视为表示为相邻列表的数据结构时,它就会变得清晰


您可以看到顶点:A、B、C、D、E和每个顶点/节点的相邻顶点作为这些顶点的列表。您必须“查看”所有框以检查是否已“访问”如果是循环图,或者是树状图,你只需遍历所有的子项即可。

谢谢,@Jose Francisco。我发现这个答案比当前具有最大向上投票数的答案更有帮助和见解。:)对不起,我有一个问题,但原始DFS visit例程本身不是在最里面的括号内递归调用吗?在分析中,为什么我们忽略了这一点?将图形显示为邻接列表使我真正“点击”了一下。这就是它应该如何呈现给那些对BFS/DFS的运行时分析有点困惑的人的方式。这个图表在解释这个概念时非常有用。
    private static void visitUsingDFS(Node startNode) {
        if(startNode.visited){
            return;
        }
        startNode.visited = true;
        System.out.println("Visited "+startNode.data);
        for(Node node: startNode.AdjacentNodes){
            if(!node.visited){
                visitUsingDFS(node);
            }
        }
    }