素集合データ構造とは? わかりやすく解説

Weblio 辞書 > 辞書・百科事典 > 百科事典 > 素集合データ構造の意味・解説 

素集合データ構造

(Union-Find から転送)

出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2026/11/02 23:38 UTC 版)

素集合データ構造
種類 多分木
発表時期 1964年
発明者 バーナード・ガラー、マイケル・フィッシャー
ビッグオー記法による計算量 (en) 
アルゴリズム 平均 最悪の場合
空間
6要素 a〜f での操作例。union(a, b)、union(c, d)、union(a, c) で木が連結され、find(d) の経路圧縮で d が根 a の直下に付け替えられる。続く union(b, e) では e が根 a の子になる。

表現

素集合森では、各集合の代表は木構造の根となる。各ノードは親ノードへのポインタを1つだけ持つ。根は自分自身を親として指すことで区別するのが一般的である(null ポインタなどの番兵を使う方法もある)。2つのノードが同じ集合に属する必要十分条件は、両者が属する木の根が同じことである。

アルゴリズムによっては、根のノードに木のサイズ(要素数)かランク(後述)のいずれかを補助情報として持たせる。これらは根でのみ必要なので、親ポインタを格納する領域にタグ付きポインタ(英語版)として同居させることもできる。また、ノードを配列に格納して親を添字で表す実装もよく使われる。このとき親ポインタ1つあたり log2 n ビットが必要なので、森全体では Θ(n log n) ビットを要する。[要出典]

MakeSet

MakeSet は、新しい要素 x の親を x 自身にして、1要素だけの木を作る。サイズやランクを使う場合は同時に初期化する。1回の MakeSet は定数時間であり、n 個の要素で森を初期化するには O(n) の時間がかかる。

 function MakeSet(x)
     x.parent := x
     x.size   := 1  // サイズを使う場合
     x.rank   := 0  // ランクを使う場合

Find

Find は親ノードへのリンクを根に到達するまでたどり、その根を返す。Union は2つの木を1つにする操作であり、どちらかの根が全体の根となる。以下に素朴な素集合森の実装例を擬似コードで示す。

 function MakeSet(x)
     x.parent := x
 function Find(x)
     if x.parent == x
        return x
     else
        return Find(x.parent)
 function Union(x, y)
     xRoot := Find(x)
     yRoot := Find(y)
     xRoot.parent := yRoot

この素朴な実装では、連結リストを使った場合と変わらない。なぜなら、木構造の平衡が保たれないからである。例えば要素 1, 2, …, n に対して Union(1, 2), Union(2, 3), …, Union(n−1, n) をこの順に実行すると、根が n で、1 から n までの経路が全ノードを通る1本の鎖ができ、Find(1) に O(n) かかる。しかし、これを改善する方法が2つある。

経路圧縮

経路圧縮の例。Find(a) の際に辿ったノード a, b, c の親が、すべて根 d に付け替えられる。

path compression(経路圧縮)は、Find操作を行ったときに構造を平坦化する改善である。Findで根ノードまでのリンクをたどっているとき、各ノードは同じ代表を共有しているので、それらを根ノードに直接接続できる。このため、Find は再帰的にたどったノードが全て親ノードとして根ノードを指すように変更する。こうすると木構造はより平坦になり、その後の操作が高速化される。この更新は集合の分割内容を変えないので、安全に行える。以下に Find の改良版を示す。

 function Find(x)
     if x.parent == x
        return x
     else
        x.parent := Find(x.parent)
        return x.parent

この再帰による実装は、根まで上る処理と根から戻る処理の2回の走査を行い、根までの経路を記憶する領域(上の擬似コードではコールスタック)を必要とする。根を探す走査と親を付け替える走査を同じ向きに2回行えば、記憶領域を定数にできる。

 function Find(x)
     root := x
     while root.parent != root
         root := root.parent
     while x.parent != root
         parent := x.parent
         x.parent := root
         x := parent
     return root

経路分割と経路半減

Tarjan と van Leeuwen は、最悪計算量を保ったまま、根に向かう1回の走査だけで済み実用上より効率的な Find のアルゴリズムも考案している[2]。これは経路分割(path splitting)と経路半減(path halving)と呼ばれ、どちらも経路上のノードの親を根に近いノードに付け替える。

経路分割は、経路上のすべてのノードの親を、そのノードの祖父(親の親)に付け替える。

 function Find(x)
     while x.parent != x
         (x, x.parent) := (x.parent, x.parent.parent)
     return x

経路半減は、経路上の1つおきのノードだけの親を祖父に付け替える。

 function Find(x)
     while x.parent != x
         x.parent := x.parent.parent
         x := x.parent
     return x

経路分割・経路半減は、再帰や経路の記憶を必要とせず、ループ1回で書けるため実装が簡潔である。

Union

MakeSet により8個のシングルトンを作った状態。
いくつかの Union を行った後の状態。いくつかの集合が1つにまとまっている。

Union(x, y) は、まず Find で x と y それぞれの根を求める。根が同じなら何もしない。異なれば、一方の根の親をもう一方の根にして2つの木を連結する。どちらを親にするかは以降の操作の計算量に影響し、無頓着に選ぶと木が高くなりすぎる。木の高さを抑えるため、効率的な実装では union by size か union by rank を用いる。どちらも、根に親ポインタ以外の情報を持たせて、どちらの根を新しい親にするかを決める。

サイズによる併合

union by size(サイズによる併合)では、根が部分木の要素数(自分自身を含む子孫の数)を持ち、Union で要素数が大きい方の木の根を全体の根にする。要素数が等しければどちらでもよい。併合後の根のサイズは、2つのサイズの和に更新する。

 function Union(x, y)
     xRoot := Find(x)
     yRoot := Find(y)
     if xRoot == yRoot
         return  // x と y は既に同じ集合に属している
     // xRoot の方が要素数が大きくなるようにする
     if xRoot.size < yRoot.size
         (xRoot, yRoot) := (yRoot, xRoot)
     yRoot.parent := xRoot
     xRoot.size   := xRoot.size + yRoot.size

この方法では、要素が属する木の根までの深さは併合のたびに増えても、そのたびに属する木の要素数が2倍以上になるため、高さが log2 n を超えない[4]。

ランクによる併合

union by rank(ランクによる併合)は、Union操作で常に大きい木の方が全体の根になるように連結するものである。木の大きさを評価するにはrank を用いる。これは、1要素の木の rank を 0 とし、同じ rank r の木を連結したとき、全体の根とした方の木の rank を r+1 とするものである。rank は木の高さの上限であるが、高さそのものではない。高さは Find の経路圧縮によって変化するので、高さを正確に保つ代わりに、rank を保持して更新の手間を避けている。このようにするだけで、MakeSet、Union、Find 操作の償却実行時間は

クラスカル法で最小全域木を求める際の Union-Find の動作例。

素集合データ構造は集合の分割をモデル化したもので、例えば無向グラフの連結成分などに対応する。したがって、2つの頂点が同じ成分に属するかどうかを調べたり、新たな辺を追加することで閉道となるかを調べるのに利用できる。辺を1本ずつ追加しながら、辺の両端が既に同じ集合に属していれば閉路ができたと判定できる。

Boost Graph Library では、Incremental Connected Components 機能の実装に素集合データ構造を使っている。また、グラフの最小全域木を求めるクラスカル法の実装にもこれを使っている。クラスカル法では、辺を重みの小さい順に見ていき、その両端が異なる集合に属する場合に限り辺を採用して2つの集合を併合する。

その他の応用として、次のものが挙げられる。

  • 単一化の高性能な実装に Union-Find が用いられる[17]。
  • Hoshen–Kopelman法(英語版)(格子状のデータの連結成分のラベル付け)は Union-Find を使う。
  • 型推論アルゴリズムの実装にも利用できる。[要出典]
  • グラフから辺を削除していくクエリは、クエリを逆順に見ると辺の追加に変わるので、オフラインであれば Union-Find で処理できる。[要出典]

なお、素集合森の実装では削除ができない点に注意されたい。上述した2つの改良を施しても削除は容易ではないが、削除に対応したもっと複雑な手法が既に設計されている(「要素の削除」の節を参照)[13]。

歴史

素集合森は1964年に Galler と Fischer によって考案され[6]、考案されてからよく使われていたが、1973年にジョン・ホップクロフトとジェフリー・ウルマンが、操作毎の時間の上限が O(log* n) であることを証明した[8]。log* は反復対数であり、これも増加の遅い関数である(ただし、アッカーマン関数の逆関数ほどではない)。その後、上限(そして限定的な下限)がアッカーマン関数の逆関数であることを証明したのはロバート・タージャンだった。1975年に Tarjan が O(m α(n)) の上限を初めて証明し[1]、この上限が厳密であることも示した。1979年には、Galler-Fischer 木を含むポインタアルゴリズムの範囲での下界を示した[7]。タージャンと van Leeuwen はより効率的なワンパスの Find のアルゴリズムも考案している[2]。1989年、Fredman と Saks が、任意の素集合データ構造に対して Ω(α(n)) の下界を示し、素集合森の最適性が一般の計算モデル(セルプローブモデル)でも確定した[3]。

1991年には、Galil と Italiano が素集合データ構造に関する調査論文を発表した[13]。同じ1991年には、Richard J. Anderson と Heather Woll が、ブロックすることなく並列化した Union-Find(wait-free な並列アルゴリズム)を発表している[18]。

2007年、ACM SIGPLAN の Workshop on ML において、Sylvain Conchon と Jean-Christophe Filliâtre が素集合森データ構造の永続版を発表した。これは構造の以前のバージョンを効率的に保持するもので、計算機支援証明システムCoqを使って形式的に正しさが証明されている[15]。最も高速な実装は、非永続のアルゴリズムとほぼ同等の性能を達成しているが、計算量の解析は行われていない。

関連項目

脚注・出典

  1. 1 2 3 4 5 6 7 8 9 Tarjan, Robert Endre (1975). “Efficiency of a Good But Not Linear Set Union Algorithm”. Journal of the ACM 22 (2): 215–225. doi:10.1145/321879.321884.
  2. 1 2 3 4 Tarjan, Robert E.; van Leeuwen, Jan (1984). “Worst-case analysis of set union algorithms”. Journal of the ACM 31 (2): 245–281. doi:10.1145/62.2160.
  3. 1 2 3 M. Fredman and M. Saks. The cell probe complexity of dynamic data structures. Proceedings of the Twenty-First Annual ACM Symposium on Theory of Computing, pages 345–354. May 1989. "Theorem 5: Any CPROBE(log n) implementation of the set union problem requires Ω(m α(m, n)) time to execute m Find's and n−1 Union's, beginning with n singleton sets."
  4. 1 2 3 4 5 6 Cormen, Thomas H.; Leiserson, Charles E.; Rivest, Ronald L.; Stein, Clifford (2009). “Chapter 21: Data structures for Disjoint Sets”. Introduction to Algorithms (Third ed.). MIT Press. ISBN 978-0-262-03384-8
  5. ↑ Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. Introduction to Algorithms, Second Edition. MIT Press and McGraw-Hill, 2001. ISBN 0-262-03293-7. Chapter 21: Data structures for Disjoint Sets, pp.498–524.
  6. 1 2 Galler, Bernard A.; Fischer, Michael J. (1964-05). “An improved equivalence algorithm”. Communications of the ACM 7 (5): 301–303. doi:10.1145/364099.364331. 素集合森に関する最初の論文。
  7. 1 2 3 Tarjan, Robert Endre (1979). “A class of algorithms which require non-linear time to maintain disjoint sets”. Journal of Computer and System Sciences 18 (2): 110–127. doi:10.1016/0022-0000(79)90042-4.
  8. 1 2 Hopcroft, J. E.; Ullman, J. D. (1973). “Set Merging Algorithms”. SIAM Journal on Computing 2 (4): 294–303. doi:10.1137/0202024.
  9. ↑ Blum, Norbert (1985). “On the Single-Operation Worst-Case Time Complexity of the Disjoint Set Union Problem”. 2nd Symp. On Theoretical Aspects of Computer Science: 32–38.
  10. ↑ Alstrup, Stephen; Ben-Amram, Amir M.; Rauhe, Theis (1999). “Worst-case and amortised optimality in union-find (Extended abstract)”. Proceedings of the thirty-first annual ACM symposium on Theory of Computing. pp. 499–506. doi:10.1145/301250.301383
  11. ↑ Alstrup, Stephen; Thorup, Mikkel; Gørtz, Inge Li; Rauhe, Theis; Zwick, Uri (2014). “Union-Find with Constant Time Deletions”. ACM Transactions on Algorithms 11 (1): 6:1–6:28. doi:10.1145/2636922.
  12. ↑ Ben-Amram, Amir M.; Yoffe, Simon (2011). “A simple and efficient Union-Find-Delete algorithm”. Theoretical Computer Science 412 (4–5): 487–492. doi:10.1016/j.tcs.2010.11.005.
  13. 1 2 3 Zvi Galil and Giuseppe F. Italiano. Data structures and algorithms for disjoint set union problems, ACM Computing Surveys, Volume 23, Issue 3 (September 1991), pages 319-344. ACM Digital Library
  14. ↑ Westbrook, Jeffery R.; Tarjan, Robert E. (1989). “Amortized Analysis of Algorithms for Set Union with Backtracking”. SIAM Journal on Computing 18 (1): 1–11. doi:10.1137/0218001.
  15. 1 2 Sylvain Conchon and Jean-Christophe Filliâtre. A Persistent Union-Find Data Structure. In ACM SIGPLAN Workshop on ML, Freiburg, Germany, October 2007. 論文ページ
  16. ↑ Gabow, Harold N.; Tarjan, Robert Endre (1985). “A linear-time algorithm for a special case of disjoint set union”. Journal of Computer and System Sciences 30 (2): 209–221. doi:10.1016/0022-0000(85)90014-5.
  17. ↑ Knight, Kevin (1989). “Unification: A multidisciplinary survey”. ACM Computing Surveys 21 (1): 93–124. doi:10.1145/62029.62030.
  18. ↑ Anderson, Richard J.; Woll, Heather (1991). Wait-free Parallel Algorithms for the Union-Find Problem. 23rd ACM Symposium on Theory of Computing. pp. 370–380. doi:10.1145/103418.103458.

外部リンク




英和和英テキスト翻訳

英語⇒日本語日本語⇒英語

辞書ショートカット

すべての辞書の索引

「素集合データ構造」の関連用語

素集合データ構造のお隣キーワード

   

英語⇒日本語
日本語⇒英語
   



素集合データ構造のページの著作権

   
ウィキペディアウィキペディア
All text is available under the terms of the GNU Free Documentation License.
この記事は、ウィキペディアの素集合データ構造 (改訂履歴)の記事を複製、再配布したものにあたり、GNU Free Documentation Licenseというライセンスの下で提供されています。 Weblio辞書に掲載されているウィキペディアの記事も、全てGNU Free Documentation Licenseの元に提供されております。

©2026 GRAS Group, Inc.RSS