[Python] 素集合データ構造

Python で素集合データ構造を実装します。

素集合データ構造

wikipedia を見てみます。

素集合データ構造(そしゅうごうデータこうぞう、: disjoint-set data structure)は、データの集合素集合(互いにオーバーラップしない集合)に分割して保持するデータ構造

出典: フリー百科事典『ウィキペディア(Wikipedia)』

どういう時に使うと便利なのか良くわかりません。

素集合データ構造は、「同値関係が与えられた時に、データを同値類に分類すること」 を効率的に行うアルゴリズムです。

以下の説明が大変分かりやすかったです。

「同値関係が与えられた時に、データを同値類に分類すること」という意味で使うことが多いので注意が必要です。
たとえば、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木です。

Union-Find木のサンプルコード

atcorder に、このアルゴリズムのとても分かりやすい解説があります。

また、以下の動画を参照して、全てを -1 で初期化し、集合のサイズをルートの負数を使って表すことで実装します。

1.12 Disjoint Sets Data Structure – Weighted Union and Collapsing Find
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]

想定通りの結果を得ることができました。