Python で素集合データ構造を実装します。
素集合データ構造
wikipedia を見てみます。
素集合データ構造(そしゅうごうデータこうぞう、英: disjoint-set data structure)は、データの集合を素集合(互いにオーバーラップしない集合)に分割して保持するデータ構造。
出典: フリー百科事典『ウィキペディア(Wikipedia)』
どういう時に使うと便利なのか良くわかりません。
素集合データ構造は、「同値関係が与えられた時に、データを同値類に分類すること」 を効率的に行うアルゴリズムです。
以下の説明が大変分かりやすかったです。
「同値関係が与えられた時に、データを同値類に分類すること」という意味で使うことが多いので注意が必要です。
Union-Find木のサンプルコード
たとえば、A, B, C, D, Eの5人がいて、そこに「AさんとBさんは友達」「DさんとEさんは友達」「BさんとCさんは友達」という情報が与えられたとします。ここで「友達の友達は友達である」という条件を与えると、「A, B, C」「D, E」という二つの友達グループにわけることができます。
このように「A, B, C, D, E」という要素の集合と、「A=B, D=E, B=C」といった同値関係の集合を与えられた時に「任意の要素が同じ同値類に属しているか」という情報が欲しくなります。これを実装するのがUnion-Find木です。
atcorder に、このアルゴリズムのとても分かりやすい解説があります。
また、以下の動画を参照して、全てを -1 で初期化し、集合のサイズをルートの負数を使って表すことで実装します。
class DisjoinSet(object): def __init__(self, n): # ルートは -1。最初は全てルートで、-1に初期化。 self.S = [-1 for x in range(n)] def find(self, x): # 負の時はルート。 if (self.S[x] < 0): return x # 負でないときは再帰的にルートまで回す。 self.S[x] = self.find(self.S[x]) return self.S[x] def union(self, x, y): # それぞれのルートを探す。 x = self.find(x) y = self.find(y) # サイズの大きい方(ルートは負で、小さいほうがサイズが大きい) # がルートになる。 if self.S[x] <= self.S[y]: self.S[x] += self.S[y] self.S[y] = x else: self.S[y] += self.S[x] self.S[x] = y def same_check(self, x, y): return self.find(x) == self.find(y) def print_set(self): print (self.S) if __name__ == '__main__': disjoint_set = DisjoinSet(9) disjoint_set.union(1, 2) disjoint_set.union(3, 4) disjoint_set.union(5, 6) disjoint_set.print_set() # [-1, -2, 1, -2, 3, -2, 5, -1, -1] disjoint_set.union(2, 3) disjoint_set.print_set() # [-1, -4, 1, 1, 3, -2, 5, -1, -1] print(disjoint_set.find(4)) # 1 disjoint_set.print_set() # [-1, -4, 1, 1, 1, -2, 5, -1, -1]
想定通りの結果を得ることができました。