C++
 Computer >> コンピューター >  >> プログラミング >> C++

DFS(深さ優先探索)による有向グラフの連結性チェック ― C++プログラム解説

グラフの連結性チェックの基本概念

グラフが連結しているかどうかを調べるには、何らかの探索アルゴリズムを用いてすべてのノードを巡回してみます。探索が完了した時点で、まだ一度も訪問されていないノードが残っていれば、そのグラフは連結ではないと判断できます。

DFS(深さ優先探索)による有向グラフの連結性チェック ― C++プログラム解説

有向グラフの場合のポイント

無向グラフと異なり、有向グラフの場合はすべてのノードを起点として探索を行う必要があります。理由は、あるエッジが外向きの辺しか持たず、内向きの辺を持たないケースが存在するためです。そのようなノードは、他のどのノードを出発点としても到達できない可能性があります。

本記事では、探索アルゴリズムとして再帰的なDFS(深さ優先探索)を採用します。

入力と出力

入力:グラフの隣接行列

01000
00100
00011
10000
01000

出力:The Graph is connected.(グラフは連結しています)

アルゴリズム

traverse(u, visited)

入力:開始ノード u、および訪問済みノードを記録するための配列 visited。

出力:u から接続されているすべての頂点を巡回します。

Begin
   mark u as visited
   for all vertex v, if it is adjacent with u, do
      if v is not visited, then
         traverse(v, visited)
   done
End

isConnected(graph)

入力:判定対象となるグラフ。

出力:グラフが連結であれば true、そうでなければ false。

Begin
   define visited array
   for all vertices u in the graph, do
      make all nodes unvisited
      traverse(u, visited)
      if any unvisited node is still remaining, then
         return false
   done
   return true
End

C++サンプルコード

#include<iostream>
#define NODE 5
using namespace std;
int graph[NODE][NODE] = {{0, 1, 0, 0, 0},
    {0, 0, 1, 0, 0},
    {0, 0, 0, 1, 1},
    {1, 0, 0, 0, 0},
    {0, 1, 0, 0, 0}};
void traverse(int u, bool visited[]) {
    visited[u] = true;          // 頂点uを訪問済みとしてマーク
    for(int v = 0; v<NODE; v++) {
        if(graph[u][v]) {       // uとvが隣接している場合
            if(!visited[v])     // vが未訪問なら再帰的に探索
                traverse(v, visited);
        }
    }
}
bool isConnected() {
    bool *vis = new bool[NODE];
    // すべての頂点uを起点として、全ノードが訪問できるかを確認
    for(int u = 0; u < NODE; u++) {
        for(int i = 0; i<NODE; i++)
            vis[i] = false;     // 初期状態では全ノード未訪問とする
        traverse(u, vis);
        for(int i = 0; i<NODE; i++) {
            if(!vis[i])         // 探索で訪問できなかったノードがあれば非連結
                return false;
        }
    }
    return true;
}
int main() {
    if(isConnected())
        cout << "The Graph is connected.";
    else
        cout << "The Graph is not connected.";
}

実行結果

The Graph is connected.

計算量について

この手法では、各ノードを起点にDFSを1回ずつ実行するため、時間計算量は O(V × (V + E)) となります。Vは頂点数、Eはエッジ数です。単純な隣接行列を用いた実装では、探索ごとに O(V²) の計算が発生する点にも留意してください。より大規模なグラフでは、隣接リスト表現や強連結成分分解(Kosaraju法など)を組み合わせると効率的に判定できます。

  1. BFS(幅優先探索)で無向グラフの連結性を判定するC++プログラム

    グラフの連結性とは グラフが連結(接続)されているかどうかを調べるには、何らかのグラフ探索アルゴリズムを使って、すべてのノードを訪問できるかどうかを確認します。探索を完了した時点で未訪問のノードが1つでも残っていれば、そのグラフは連結していないことになります。 無向グラフの場合は、任意の1つのノードを選び、そこから探索を開始します。本記事では、探索アルゴリズムとして幅優先探索(BFS:Breadth-First Search)を採用しています。 入力と出力の例 入力 − グラフの隣接行列 0110010110110110110100110 出力 − 「グラフは連結しています。」 アルゴリズム

  2. 有向グラフにオイラー閉路が含まれているかどうかを判定するC++プログラム

    オイラー閉路(オイラー回路)とは、グラフ上のすべての辺をちょうど1回ずつ通過できる経路のことです。このとき、同じ頂点を何度通っても構いません。オイラー閉路はオイラー路(Euler Path)の特別な形であり、オイラー路の始点がそのまま終点ともつながっている場合を指します。 ある有向グラフがオイラー閉路を持つかどうかを判定するには、次の2つの条件を満たしている必要があります。 グラフが連結であること(任意の頂点から他のすべての頂点へ到達できること)。 すべての頂点において、入次数と出次数が等しいこと。 入力 − グラフの隣接行列 01000 00100 00011 10000 0010