頂点の隣接する各エッジを通過する時間の複雑さはO(N)
と言えます。ここで、Nは隣接するエッジの数です。そのため、Vの頂点の時間の複雑さはO(V*N)
= O(E)
になります。ここで、Eはグラフ内のエッジの総数です。キューから頂点を削除したりキューに追加したりするのはO(1)
であるため、BFSの全体的な時間の複雑さにO(V+E)
として追加される理由です。
これが、Breadth First Search a.k.a 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(| V | + | E |)であり、O(V * E)ではないことがわかります。
隣接リスト
V E
v0:{v1,v2}
v1:{v3}
v2:{v3}
v3:{}
BFSの仕組みを段階的に操作する
ステップ1:
隣接リスト:
V E
v0: {v1,v2} mark, enqueue v0
v1: {v3}
v2: {v3}
v3: {}
ステップ2:
隣接リスト:
V E
v0: {v1,v2} dequeue v0;mark, enqueue v1,v2
v1: {v3}
v2: {v3}
v3: {}
ステップ3:
隣接リスト:
V E
v0: {v1,v2}
v1: {v3} dequeue v1; mark,enqueue v3
v2: {v3}
v3: {}
ステップ4:
隣接リスト:
V E
v0: {v1,v2}
v1: {v3}
v2: {v3} dequeue v2, check its adjacency list (v3 already marked)
v3: {}
ステップ5:
隣接リスト:
V E
v0: {v1,v2}
v1: {v3}
v2: {v3}
v3: {} dequeue v3; check its adjacency list
ステップ6:
隣接リスト:
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はエッジの数です。
各頂点は最大で1回キューに入れられ、キューから取り出されます。
隣接するすべての頂点のスキャン O(|E|) 隣接リストの長さの合計は | E |。
したがって、BFSの時間の複雑さは O(|V|+|E|) 時間の複雑さ。
O(1)
操作をL
回実行すると、O(L)
の複雑さが生じます。したがって、キューから頂点を削除したりキューに追加したりするのはO(1)ですが、V
頂点に対してこれを行うと、O(V)
の複雑さが生じます。したがって、O(V) + O(E) = O(V+E)
ここでの他の回答は、BFSの実行方法と分析方法を示す素晴らしい仕事です。元の数学的分析を再検討して、具体的には、推論によって真の値よりも低い推定値が得られる場所を示したかったのです。
分析は次のようになります。
ここで正しい見積もりをすることに非常に近いです。問題は、欠落しているV用語の由来です。ここでの問題は、奇妙なことに、O(V)・O(E/V)= O(E)とは言えないということです。
ノードあたりの平均作業量がO(E/V)であることは完全に正しいです。つまり、完了した作業の合計同義的にはE/Vの倍数によって上から制限されます。BFSが実際に実行していることを考えると、ノードごとに実行される作業はおそらくcのようになります。1 + c2E/V、ノードごとに行われた作業のベースライン量があるため(ループの設定、基本条件のチェックなど)、これはcによって説明されます1 項に加えて、訪問したエッジの数に比例する作業量(E/V、Edgeごとに実行された作業の時間)。これにVを掛けると、
V・(c1 + c2E/V)
= c1V + c2E
=Θ(V + E)
ここで起きているのは、big-Oが便利に無視できるような素敵な低次の用語が実際に重要であるため、それらを簡単に破棄できないことです。だから、数学的には少なくとも何が起こっているのでしょう。
実際にここで起こっているのは、グラフにいくつのエッジがあっても、それらのエッジとは関係なく各ノードに対してやらなければならない作業のベースラインがあることです。これは、コアifステートメントの実行、ローカル変数のセットアップなどのようなことを行うためのセットアップです。