特に、次のサイトのこの例のように、頂点から到達できるノードに向けられたエッジを持つノードがある場合、BFSおよびDFS O(V + E)の実行時間はなぜですか
http://www.personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/GraphAlgor/depthSearch.htm
Eは、G = {V、E}のように、グラフ内のすべてのエッジのセットです。したがって、| E |グラフ内のすべてのエッジの数です。
これだけで、全体的な複雑さが| V |にならないことを確信させるのに十分なはずです。これは、各頂点のグラフのすべてのエッジを反復処理していないためです。
隣接リスト表現では、各頂点vについて、それに隣接するノードのみを走査します。
| V | | V | + | E |の係数実行されたキュー操作の数に起因するようです。
アルゴリズムの複雑さは、使用するデータ構造に依存することに注意してください。事実上、グラフの表現に存在する各情報にアクセスしているため、グラフのマトリックス表現では、複雑度はVの2乗になります。
コーメンから引用して、
「エンキューおよびデキューの操作には、O(1)時間を要するため、キュー操作に費やされる合計時間はO(V)です。各頂点の隣接リストは、頂点がすべての隣接リストの長さの合計がΘ(E)であるため、隣接リストのスキャンに費やされる合計時間はO(E)です。初期化のオーバーヘッドはO( V)、したがってBFSの合計実行時間はO(V + E)です。」
この問題は私の時間の4時間ほどを消費しますが、最終的には写真を撮る簡単な方法があると思います。最初はO(V * E)と言いたくなりました。
Cormenで見つけたアルゴリズムを要約すると、それは http://www.personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/GraphAlgor/breadthSearch.htm で同じです:
for(vi in V)Some O(1) instructions for(e in Adj(vi))){Some O(1) instructions}
問題は、ここで実行される命令の数です。それはSigma-Sum(Adj(vi))になり、この値の上限は2 | E |になります。
最初は、内部ループと外部ループの反復回数を乗算することを自動的に考えますが、この場合、内部ループの反復の合計数は外部反復子の関数であるため、乗算は不可能です。
すべてのEdgeに最大2回アクセスします。 Eエッジがあります。そのため、2 * E Edgeの訪問操作が行われます。さらに、ノードにはエッジがない、つまり次数0のノードがあります。最大でV個のノードが存在できます。したがって、複雑さはO(2 * E + V)= O(E + V)
| V |を反復処理します最大で| V |のノード回。 | E |の上限があるためグラフ内の合計エッジ、最大で| E |をチェックします。エッジ。異なる頂点には隣接するノードの数が異なるため、cannot単に| V | * | E |を乗算します(つまり、各頂点について| E |エッジをトラバースしますが、これは真ではありません。| E |はすべてのノードのエッジの総数です)、Vノードをチェックし、合計Eをチェックしますエッジ。最後に、O(| V | + | E |)があります
DFSについても同様です。すべての頂点隣接リストをループし、DFS(v)が呼び出されていない場合は呼び出します。つまり、| V |が発生します。タイムステップに加えて、隣接ノードを訪問するためにかかった時間(本質的に、これらはエッジを形成し、合計| E |エッジがあるため、O(V + E)時間です。
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);
}
}
}