差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
次のリビジョン両方とも次のリビジョン
programming:algorithm:data_structure:union_find_tree [2017/08/07] ikatakosprogramming_algorithm:data_structure:union_find_tree [2019/08/25] ikatakos
行 1: 行 1:
 ======Union-Find木====== ======Union-Find木======
  
-  * 要素が$n$個ある +  * [[wpjp>素集合データ構造]] 
-  * 各要素はグループに1つだけ属する + 
-    * 「互いに素な集合」 +  * 要素が $n$ 個あり、各要素は互いに素なグループに1つだけ属する 
-    * 例: 生徒は必ず何かのクラブ活動に入り、かつ、兼部は認められていない +    * 例: 生徒は必ず何かのクラブ活動に入り、兼部は認められていない 
-  * グループは統合される可能性がある分割はされない+  * グループは統合される可能性があるが、分割はされない
  
 という前提で、 という前提で、
行 16: 行 16:
   * [[https://www.slideshare.net/chokudai/union-find-49066733|Union find(素集合データ構造)]]   * [[https://www.slideshare.net/chokudai/union-find-49066733|Union find(素集合データ構造)]]
   * [[http://www.geocities.jp/m_hiroi/light/pyalgo61.html|Algorithms with Python / Union-Find]]   * [[http://www.geocities.jp/m_hiroi/light/pyalgo61.html|Algorithms with Python / Union-Find]]
 +  * [[http://dai1741.github.io/maximum-algo-2012/docs/minimum-spanning-tree/|最小全域木(クラスカル法とUnionFind) - アルゴリズム講習会]]
  
 =====アルゴリズム===== =====アルゴリズム=====
  
-  * 各グループの要素から、リーダーを1つ決める +  * 各グループの要素から、"代表"を1つ決める 
-  * リーダー以外の要素は、親となる要素を1つずつ持つ +  * 代表以外の要素は、""となる要素を1つずつ持つ 
-    * つまり、1つのグループを、リーダーを根とした木構造で表現する +    * どの要素からでも、親の親の親……を辿っていけば自身の代表に着くようにする 
-    * 親の親の親……を辿っていけばリーダーに着く+    * つまり、グループを、代表を根とした木構造で表現する
  
-  * ${\rm Union}(x, y)$は、yリーダーの親xリーダーにする +<code> 
-  * ${\rm Find}(x, y)$は、xとyから親を辿って同じリーダーに行き着くか調べる+    1    4    8  ←代表 
 +  ↗↑    ↑ 
 + 2 5    6 
 + ↑     ↗  ↖ 
 + 3    7    9 
 +</code> 
 + 
 +  * ${\rm Union}(x, y)$は、一方代表、もう一方代表につなぐ(親にする 
 +  * ${\rm Find}(x, y)$は、xとyから親を辿って同じ代表に行き着くか調べる
  
-=====高速化の工夫=====+====高速化の工夫====
  
-  * 木の階層を低く保たないと、リーダーを見つける処理に時間がかかる +  * 木の階層を低く保たないと、代表を見つける処理に時間がかかる 
-    * はやい例ではどの要素からも、1回調べたらにたどり着ける +    * 以下の例、はやい例では2~4のどの要素からも、1回調べたら代表にたどり着ける 
-    * おそい例では、4からだと、4→3→2→1と3回辿らないとに着かない+    * おそい例では、4からだと、4→3→2→1と3回辿らないと代表に着かない 
 +    * できるだけ、はやい例のような状態を保ちた
 <code> <code>
 ◎はやい  ×おそい ◎はやい  ×おそい
行 43: 行 53:
 </code> </code>
  
-  * 経路圧縮 +===経路圧縮=== 
-    * 一度調べた経路上にある要素は、全て親をリーダーに書き換える +  * 一度調べた経路上にある要素は、全て親を代表に書き換える 
-      * おそい例において、4調べて1がリーダーだとわかったら、3,4の親を1に書き換える +    * おそい例において、4→3→2→1と調べて1が代表だとわかったら、2~4の親を1に書き換える 
-  ランク + 
-    * 木の深さ情報(ランク)を記録しておく +===ランク=== 
-    * Unionでは、ランクの低い方のグループを、高い方のグループに吸収+  * 木の最大深さ(ランク)を記録しておく 
 +  * Union、ランクの低い方のグループを、高い方のグループにつなぐ 
 +    * 互いのランクが等しい場合を除き、ランクが高くならずに済む(等しい場合のみ1増える) 
 + 
 +<code> 
 +この2つをUnionする場合| こうすると   | 低い方に高い方をつなげると 
 + Rank:   Rank:    | Rank:4のまま | Rank:5になっちゃう 
 +   1        5            5      |     1 
 + ↗↑↖      ↑            ↑↖    | ↗↗↑↖ 
 +2 3 4     6            1 6   |23 4 5 
 +             ↑        ↗↗↑ ↑          ↑ 
 +             7       | 23 4 7          6 
 +             ↑               ↑          ↑ 
 +             8               8          7 
 +                      |              |        ↑ 
 +                      |              |        8 
 +</code> 
 + 
 +ランクは深でなく、要素数で考えこともある 
 + 
 +  * 例: 要素数100,最大深さ2のグループAと、要素数5,最大深さ4のグループB 
 +    * AにBを繋ぐとBの5個の要素の深さが1ずつ増え、最大深さは5 
 +    * BにAを繋ぐとAの100個の要素の深さが1ずつ増え、最大深さは4 
 +    * 局所的に最大深さが深くなろうが、100個の要素の深さが1ずつ増える方が効率悪くない?という考え方か 
 +  * A.V.Aho, John E. Hopcroft, Jeffrey D. Ulman, 『データ構造とアルゴリズム』, 培風館, 1987
  
 =====実装===== =====実装=====
 +
 +  * 要素数 $n$ の配列''**table**''を作り、-1で初期化する
 +    * ${\rm table}[x]$ が負ならば、$x$ は代表。絶対値はランクを示す
 +    * ${\rm table}[x]$ が非負ならば、$x$ は代表でない。値は $x$ の親を示す
 +    * 最初は、$n$個の要素が別々の$n$個のグループに所属している状態
 +  * ''_root''で代表を再帰的に探索する
 +    * ついでに経路圧縮を行う
  
 <sxh python> <sxh python>
行 96: 行 137:
     print(ins.find(0, 3))  # True     print(ins.find(0, 3))  # True
  
 +</sxh>
 +
 +統合時、どちらを根にするかの基準をグループの深さで無く「要素数」とした実装。unionの部分が少し変化。
 +
 +<sxh python>
 +
 +class UnionFind:
 +    def __init__(self, n):
 +        self.table = [-1] * n
 +
 +    def _root(self, x):
 +        if self.table[x] < 0:
 +            return x
 +        else:
 +            self.table[x] = self._root(self.table[x])
 +            return self.table[x]
 +
 +    def find(self, x, y):
 +        return self._root(x) == self._root(y)
 +
 +    def union(self, x, y):
 +        r1 = self._root(x)
 +        r2 = self._root(y)
 +        if r1 == r2:
 +            return
 +        d1 = self.table[r1]
 +        d2 = self.table[r2]
 +        if d1 <= d2:
 +            self.table[r2] = r1
 +            self.table[r1] += d2
 +        else:
 +            self.table[r1] = r2
 +            self.table[r2] = r1
 </sxh> </sxh>
programming_algorithm/data_structure/union_find_tree.txt · 最終更新: 2023/05/26 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0