web-dev-qa-db-ja.com

有向非循環グラフで最も低い共通祖先を見つけるアルゴリズム?

次のような有向非循環グラフを想像してください。

  • 「A」はルートです(ルートは常に1つだけです)
  • 各ノードはその親を知っています
  • ノード名は任意です-それらから推測できるものはありません
  • 別のソースから、ノードがAからGの順序でツリーに追加されたことがわかります(例:バージョン管理システムでのコミット)

Directed Acyclic Graph

2つの任意のノードの最小共通祖先(LCA)を決定するために使用できるアルゴリズムはどれですか。たとえば、次の共通祖先です。

  • BとEはB
  • DとFはB

注意:

  • ルートから特定のノードへの単一のパスがあるとは限りません(たとえば、「G」には2つのパスがあります)。したがって、ルートから2つのノードへのパスを単純に トラバースして検索することはできません。最後の等しい要素
  • ツリー、特にバイナリツリーのLCAアルゴリズムを見つけましたが、ノードは複数の親を持つことができるため、ここでは適用されません(つまり、これはツリーではありません)。
28
Andrew Swan

Den Romanのリンク は有望に思えますが、少し複雑に思えたので、別の方法を試しました。これが私が使った簡単なアルゴリズムです:

xyの2つのノードでLCA(x、y)を計算するとします。各ノードには値colorcountが必要です。 whiteおよびに初期化されます。

  1. xのすべての祖先をblueとして色付けします(- [〜#〜] bfs [〜#〜 ]
  2. すべてのblueの祖先をyの先祖にredとして色付け(BFSを再び)
  3. グラフ内のredノードごとに、その親のcountを1つ増やします

count値がに設定されている各redノードがソリューションです。

グラフによっては、複数のソリューションが存在する場合があります。たとえば、次のグラフについて考えてみます。

DAG example where LCA can have two values

LCA(4,5)の可能な解は1と2です。

3つ以上のノードのLCAが必要な場合でも機能します。ノードごとに異なる色を追加する必要があるだけです。

同じ問題の解決策を探していたところ、次の論文で解決策を見つけました。

http://dx.doi.org/10.1016/j.ipl.2010.02.014

つまり、最も低い共通の祖先を探すのではなく、このホワイトペーパーで定義する最も低いSINGLEの共通の祖先を探します。

5
Steven Obua

私はそれが古い質問でかなり良い議論であることを知っていますが、解決するいくつかの同様の問題があったので、 JGraphTLowest Common Ancestor アルゴリズムに出くわしたので、これは助けて:

2
Hamid Nazari

グラフでxとyの祖先を検索するとします。

ベクトルの配列を維持する-parents(各ノードの親を格納する)。

  1. 最初にbfs(各頂点の親を保存し続ける)を実行し、xのすべての祖先を見つけ(xの親を見つけてparentsを使用し、xのすべての祖先を見つけます)、それらをベクトルに保存します。また、各親の深度をベクターに格納します。

  2. 同じ方法を使用してyの祖先を見つけ、それらを別のベクトルに格納します。これで、xとyの祖先をそれぞれ深度とともに格納する2つのベクトルがあります。

  3. LCAは共通の祖先であり、最大の深さがあります。深さは、ルート(in_degree = 0の頂点)からの最長距離として定義されます。これで、ベクトルを深さの降順でソートして、LCAを見つけることができます。この方法を使用すると、複数のLCA(存在する場合)を見つけることもできます。

1
shiwang

ちょうどいくつかの野生の考え。両方の入力ノードをルートとして使用し、2つのBFSを段階的に同時に実行するのはどうでしょうか。特定のステップで、BLACKセットに重複がある場合(訪問済みノードの記録)、アルゴリズムが停止し、重複するノードがLCAになります。このように、他の一般的な祖先は、私たちが発見したものよりも距離が長くなります。

1
Ning

グラフにサイクルがある場合、「祖先」は大まかに定義されます。おそらく、DFSまたはBFSのツリー出力の祖先ですか?あるいは、「祖先」とは、EBからのホップ数を最小化するダイグラフのノードを意味しますか?

複雑さについて心配していなければ、すべてのノードからEBの両方へのA *(またはダイクストラの最短パス)を計算できます。 EBの両方に到達できるノードの場合、PathLengthToE + PathLengthToBを最小化するノードを見つけることができます。

編集:あなたはいくつかのことを明確にしたので、私はあなたが探しているものを理解していると思います。

ツリーを「上に」上ることしかできない場合は、EからBFSを実行し、BからBFSを実行することをお勧めします。グラフ内のすべてのノードには、2つの変数が関連付けられています:BからのホップとEからのホップ。 BEの両方にグラフノードのリストのコピーを持たせます。 BのリストはBからのホップでソートされ、EのリストはEからのホップでソートされます。

Bのリスト内の各要素について、Eのリスト内で検索してみてください。 Bからのホップ+ Eからのホップでソートされた3番目のリストに一致を配置します。 Bのリストを使い果たした後、3番目にソートされたリストの先頭にLCAが含まれているはずです。これにより、1つのソリューション、複数のソリューション(BのBFS順序によって任意に選択)、またはソリューションなしが可能になります。

0
AndyG

http://www.gghh.name/dibtp/2014/02/25/how-does-Mercurial-select-the-greatest-common-ancestor.html

このリンクはそれがMercurialでどのように行われるかを説明します-基本的な考え方は、指定されたノードのすべての親を見つけ、ルートからの距離ごとにそれらをグループ化し、それらのグループで検索を行うことです。

0
Den Roman

DAG(有向非巡回グラフ)でLCAを見つけるには、まったく同じものが必要です。 LCA問題はRMQ(範囲最小クエリ問題)に関連しています。

LCAをRMQに削減し、有向非循環グラフから任意の2つのノードの必要なLCAを見つけることができます。

私は このチュートリアル 詳細と良いとわかりました。私もこれを実装する予定です。

0
Forhad

私はO(| V | + | E |)時間複雑性ソリューションを提案しています。このアプローチは正しいと思います。それ以外の場合は修正してください。

有向非循環グラフを考えると、2つの頂点vとwのLCAを見つける必要があります。

ステップ1:bfs http://en.wikipedia.org/wiki/Breadth-first_search を使用し、時間の複雑さO(| V | + | E |)を使用して、ルート頂点からすべての頂点の最短距離を見つけます。各頂点の親を見つけます。

ステップ2:ルート頂点に到達するまで、親を使用して両方の頂点の共通の祖先を検索します。時間の複雑さ-2 | v |

ステップ3:LCAは、最大の最短距離を持つ共通の祖先になります。

したがって、これはO(| V | + | E |)時間複雑度アルゴリズムです。

私が間違っている場合、または他の提案が歓迎されている場合は、私を修正してください。

0
Nitish Jain