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

C++でグラフの関節点(カット頂点)を検出するプログラム

グラフの頂点連結性を調べるには、そのグラフの関節点を特定する必要があります。関節点とは、その頂点(およびそれに接続する辺)を取り除くとグラフが分断されてしまう頂点のことです。非連結な無向グラフの場合、ある頂点を削除したときに連結成分の数が増加するならば、その頂点は関節点であるとみなされます。

アルゴリズム

ここではDFS(深さ優先探索)を用いて関節点を検出します。DFSにおいて、頂点wが次のいずれかの条件を満たすとき、wは関節点となります。

  1. wがDFS木の根であり、かつ2つ以上の子を持つ。
  2. wがDFS木の根ではなく、子xを持ち、wを根とする部分木内のどの頂点からも、木におけるwの祖先への後退辺が存在しない。

サンプルコード

#include<iostream>
#include <list>
#define N -1
using namespace std;
class G {
    int n;
    list<int> *adj;
    // 関数の宣言
    void APT(int v, bool visited[], int dis[], int low[],
    int par[], bool ap[]);
public:
    G(int n); // コンストラクタ
    void addEd(int w, int x);
    void AP();
};
G::G(int n) {
    this->n = n;
    adj = new list<int>[n];
}
// グラフに辺を追加
void G::addEd(int w, int x) {
    adj[x].push_back(w); // u を v のリストに追加
    adj[w].push_back(x); // v を u のリストに追加
}
void G::APT(int w, bool visited[], int dis[], int low[], int
par[], bool ap[]) {
    static int t=0;
    int child = 0; // DFS木の子の数を0で初期化
    // 現在のノードを訪問済みとしてマーク
    visited[w] = true;
    dis[w] = low[w] = ++t;
    list<int>::iterator i;
    // すべての隣接頂点を走査
    for (i = adj[w].begin(); i != adj[w].end(); ++i) {
        int x = *i; // x は現在の隣接頂点
        if (!visited[x]) {
            child++;
            par[x] = w;
            APT(x, visited, dis, low, par, ap);
            low[w] = min(low[w], low[x]);
            // 次の場合、w は関節点となる:
            // w がDFS木の根で、2つ以上の子を持つ場合
            if (par[w] == N && child > 1)
                ap[w] = true;
            // w が根でなく、ある子のlow値が w の発見値以上の場合
            if (par[w] != N && low[x] >= dis[w])
                ap[w] = true;
        }
        else if (x != par[w]) // low値を更新
        low[w] = min(low[w], dis[x]);
    }
}
void G::AP() {
    // すべての頂点を未訪問としてマーク
    bool *visited = new bool[n];
    int *dis = new int[n];
    int *low = new int[n];
    int *par = new int[n];
    bool *ap = new bool[n];
    for (int i = 0; i < n; i++) {
        par[i] = N;
        visited[i] = false;
        ap[i] = false;
    }
    // 頂点 i を根とするDFS木に対して APT() を呼び出し、関節点を検出
    for (int i = 0; i < n; i++)
        if (visited[i] == false)
            APT(i, visited, dis, low, par, ap);
    // 関節点を出力
    for (int i = 0; i < n; i++)
        if (ap[i] == true)
            cout << i << " ";
}
int main() {
    cout << "\nArticulation points in first graph \n";
    G g1(5);
    g1.addEd(1, 2);
    g1.addEd(3, 1);
    g1.addEd(0, 2);
    g1.addEd(2, 3);
    g1.addEd(0, 4);
    g1.AP();
    return 0;
}

実行結果

Articulation points in first graph
0 2

解説

この例では、頂点0と頂点2を取り除くとグラフが複数の連結成分に分断されるため、これらが関節点として検出されています。本アルゴリズムは各頂点を一度だけ訪問するDFSに基づいているため、計算量は頂点数V・辺数Eに対してO(V+E)となり、大規模なグラフでも効率的に関節点を求められます。

  1. グラフ内のスーパー頂点を見つけるC++プログラムの解説

    問題の概要n個の頂点を持つグラフが与えられていると仮定しましょう。頂点には1からnまでの番号が付けられており、配列「edges」に含まれる辺によって互いに接続されています。さらに、各頂点は1からnの範囲の数値である「x」という値を持ち、その値は配列「values」で与えられます。このとき、グラフの中から「スーパー頂点(super vertex)」と呼ばれる特別な頂点を見つけ出す必要があります。頂点iがスーパー頂点であるとは、頂点1から頂点iへの最短経路上に、i番目の頂点と同じ「x」の値を持つ頂点が存在しないことを意味します。この条件を満たすすべての頂点を出力してください。たとえば、入力が n

  2. 【C++】グラフ内の橋(ブリッジエッジ)の数を検出するプログラムの解説

    ブリッジエッジ(橋)とは? 重みなし無向グラフにおけるブリッジエッジ(橋)とは、その辺を取り除いたときにグラフが非連結(複数の連結成分に分断される)となるような辺のことです。本記事では、n個の頂点とm個の辺からなるグラフが与えられたとき、その中に含まれるブリッジの数を求めるC++プログラムを紹介します。なお、対象となるグラフには平行辺や自己ループは含まれないものとします。 問題の例 例として、n = 5、m = 6、edges = {{1, 2}, {1, 3}, {2, 3}, {2, 4}, {2, 5}, {3, 5}} という入力が与えられた場合を考えてみましょう。この場合の出力は