web-dev-qa-db-ja.com

グラフのアーティキュレーションポイントまたはカット頂点を見つけるためのアルゴリズムの説明

ネットを検索しましたが、グラフのすべてのアーティキュレーション頂点を見つけるためのDFSアルゴリズムの説明は見つかりませんでした。ウィキページすらありません。

読んでから、ここから基本的な事実を知ることができました。 [〜#〜] pdf [〜#〜]

各ノードには変数があり、実際には後端を見て、ルートノードに向かって最も近くて一番上のノードを見つけています。すべてのエッジを処理した後、それが見つかります。

しかし、DFSの実行中に各ノードでこのdown&up変数を見つける方法がわかりません。この変数は正確に何をしていますか?

アルゴリズムを説明してください。

ありがとう。

26
Ashish Negi

アーティキュレーションの頂点を見つけることは、DFSのアプリケーションです。

手短に、

  1. グラフにDFSを適用します。 DFSツリーを取得します。
  2. 前にアクセスしたノードは、そのノードが到達して後でアクセスしたノードの「親」です。
  3. ノードのいずれかの子にその親の祖先へのパスがない場合、このノードを削除すると、この子がグラフから分離されます。
  4. 例外があります:ツリーのルート。複数の子がある場合、それは明確なポイントであり、そうでない場合はそうではありません。

ポイント3は、本質的にこのノードがアーティキュレーションポイントであることを意味します。

子の場合、ノードの祖先へのこのパスは、ノードまたはその子のいずれかからのバックエッジを経由します。

これはすべてこの [〜#〜] pdf [〜#〜] で詳しく説明されています。

38
Ashish Negi

このアルゴリズムがどのように機能するかについて直感的に理解し、Biコンポーネントとブリッジを出力するコメント付きの擬似コードを提供しようとします。

実際に、アーティキュレーションポイントのブルートフォースアルゴリズムを開発するのは簡単です。頂点を取り出して、グラフでBFSまたはDFSを実行するだけです。接続されたままの場合、頂点はアーティキュレーションポイントではありません。これはO(V(E+V)) = O(EV)時間で実行されます。課題は、これを線形時間で実行する方法です(つまり、O(E+V))。

アーティキュレーションポイントは、2つ(またはそれ以上)のサブグラフを接続します。これは、あるサブグラフから別のサブグラフへのエッジがないことを意味します。したがって、これらのサブグラフのいずれかにいて、そのノードにアクセスしていると想像してください。ノードにアクセスすると、フラグを立ててから、利用可能なEdgeを使用して次のフラグなしノードに移動します。あなたがこれをしている間、あなたはまだ同じサブグラフ内にいることをどのように知っていますか?ここでの洞察は、同じサブグラフ内にいる場合、フラグのないノードにアクセスしているときに、最終的にエッジを通してフラグの付いたノードが表示されるということです。これはバックエッジと呼ばれ、サイクルがあることを示します。バックエッジを見つけるとすぐに、そのフラグ付きノードから現在アクセスしているノードまでのすべてのノードがすべて同じサブグラフの一部であり、間にアーティキュレーションポイントがないことを確信できます。バックエッジが表示されない場合、これまでにアクセスしたすべてのノードはすべてアーティキュレーションポイントです。

したがって、頂点を訪問し、同じサブグラフ内のcurrently-being-visitedノードとしてバックエッジのターゲット間のすべてのポイントをマークするアルゴリズムが必要です。サブグラフ内にサブグラフがあることは明らかであるため、これまでにある最大のサブグラフを選択する必要があります。これらのサブグラフは、バイコンポーネントと呼ばれます。このアルゴリズムを実装するには、各バイコンポーネントに、これまでに訪れた頂点の数のカウントとして初期化されるIDを割り当てます。後端を見つけたら、バイコンポーネントIDをこれまでに見つかった最低の値にリセットできます。

明らかに2つのパスが必要です。最初のパスでは、もしあれば、各頂点から後端までどの頂点を見ることができるかを把握したいと思います。 2番目のパスでは、反対方向に頂点を訪問し、最小のバイコンポーネントID(つまり、子孫からアクセス可能な最も早い祖先)を収集します。 DFSは当然ここに適合します。 DFSでは、最初にダウンしてから再びアップするため、上記の両方のパスを単一のDFSトラバーサルで実行できます。

これ以上苦労することなく、擬似コードを次に示します。

time = 0
visited[i] = false for all i
GetArticulationPoints(u)
    visited[u] = true
    u.st = time++
    u.low = v.st    //keeps track of highest ancestor reachable from any descendants
    dfsChild = 0    //needed because if no child then removing this node doesn't decompose graph
    for each ni in adj[i]
        if not visited[ni]
            GetArticulationPoints(ni)
            ++dfsChild
            parents[ni] = u
            u.low = Min(u.low, ni.low)  //while coming back up, get the lowest reachable ancestor from descendants
        else if ni <> parent[u] //while going down, note down the back edges
            u.low = Min(u.low, ni.st)

    //For dfs root node, we can't mark it as articulation point because 
    //disconnecting it may not decompose graph. So we have extra check just for root node.
    if (u.low = u.st and dfsChild > 0 and parent[u] != null) or (parent[u] = null and dfsChild > 1)
        Output u as articulation point
        Output edges of u with v.low >= u.low as bridges
    output u.low as bicomponent ID
13
Shital Shah

lowの子孫のudfsnumuよりも大きい場合、uはアーティキュレーションポイントと呼ばれます。

int adjMatrix[256][256];
int low[256], num=0, dfsnum[256];

void cutvertex(int u){
    low[u]=dfsnum[u]=num++;
    for (int v = 0; v < 256; ++v)
    {
        if(adjMatrix[u][v] && dfsnum[v]==-1)
        {
            cutvertex(v);
            if(low[v]>dfsnum[u])    
                cout<<"Cut Vertex: "<<u<<"\n";
            low[u]=min(low[u], low[v]);
        }
        else{
            low[u]=min(low[u], dfsnum[v]);
        }
    }
}
0
nimit