こんにちは
PythonでのUnion-Find(素集合データ構造)の実装と使い方を参考にして
union findの使い方をみていきます。
アリ本にもでてくるデータ構造です。
上記リンクでの実装でのポイントはparentsで
親(parents)に各要素の親番号(親要素へのindex)を格納する要素自身が親つまりは根元の場合は、そのグループの要素数をマイナスで持つ
とうポイントを押さえれば、あとの理解は付いてくるはずです。
ここで少しバックグラウンドを話しましょう。
union findの祖先は
quick findです。coursera でプリンストン大学の動画はこちら
が、unionにO(N)で、実際グラフ形成にO(N^2)でquadratic(2乗)かかって 、データ大きいとだめだ
ってことで
union findです。っと行きたいところですが、まずはその前身、quick-unionを見ましょう。
union findっていうのも理解するには見ておいて損はないです。(coursera union find動画)
quick-unionはunionが速いので、すごい良さげやん!って思うんですがworseケースを考えるとまだquadraticなんですねー。
worseケースっていうのは、findのときに高〜い、一直線の木です。
そこで!
工夫をしたのが、やっときました。そうunion findです。
union-quickで、木が高いとworseになるということだったので、木を構成していくとき
つまりはunion時に木が高くならないような工夫をしましょう!というのが肝です
それをするためにはグループのノード数の数を保存しておけばいいんです
あと、もう一個の工夫、これも高さを出さないための、木を低く保つための工夫で
findの速さにつながることですね。これは経路圧縮(path compression)というもので
やはりこの動画が非常にわかりやすいです。さらに経路圧縮でさらにかなり、速くなります。
log*N(ログNスター)というか速さ、でリニア(O(N))に近い速さだそうです。
親(parents)に各要素の親番号(親要素へのindex)を格納する要素自身が親つまりは根元の場合は、そのグループの要素数をマイナスで持つ

実装
class unionfind():
def __init__(self, n):
self.n = n
self.pars = [-1] * n # 親のindexか、自信が親の場合は-(要素数)
def find(self, x):
if self.pars[x] < 0:
return x
else:
# 親が根元ではないので、根元を探す旅
oya = self.pars[x]
# #これが経路圧縮。なくてもうごくよ。有ると速い。
# self.pars[x] = self.find[oya]; return self.pars[x]
return self.find(oya)
def union(self, x, y):
X = self.find(x) # Xはxの親である
Y = self.find(y)
if X == Y: return
# 親のマージ。親のグループの小さい方に、大き方を足したいので。
if self.pars[X] > self.pars[Y]:
X, Y = Y, X
self.pars[X] += self.pars[Y]
self.pars[Y] = X
以上です