素集合の概要 (和集合検索アルゴリズム)

Disjoint セット データ構造とは何ですか?

2 つのセットが呼び出されます 素集合 共通の要素がない場合、セットの共通部分は空セットになります。

要素の重複していない、または素のサブセットを格納するデータ構造は、素セット データ構造と呼ばれます。素セットのデータ構造は、次の操作をサポートします。

  • 素のセットに新しいセットを追加します。
  • 次を使用して、素セットを単一の素セットにマージします。 連合 手術。
  • を使用して素集合の代表を見つける 探す 手術。
  • 2 つのセットが素であるかどうかを確認します。

多数の人がいて、それらに対して次のタスクが実行される状況を考えてみましょう。



  • 追加 新しい友情 関係 つまり、人 x は別の人 y の友達になります。つまり、セットに新しい要素が追加されます。
  • 個人かどうかを調べる x は個人 y の友人です (直接的または間接的な友人)

例:

10 人の個人が与えられ、a、b、c、d、e、f、g、h、i、j と言うとします。

追加する関係は次のとおりです。
a b
bd
c f

じぇ
g j

a が d の友達かどうかなどのクエリが与えられたとします。基本的には、次の 4 つのグループを作成し、グループ項目間ですぐにアクセスできる接続を維持する必要があります。
G1 = {a、b、d}
G2 = {c, f, i}
G3 = {e,g,j}
G4 = {h}

x と y が同じグループに属しているかどうかを調べます。つまり、x と y が直接的/間接的な友人であるかどうかを調べます。

個人を、属するグループに従って異なるセットに分割します。この方法はとして知られています 素集合和集合 のコレクションを維持します 素集合 そして各セットはそのメンバーの 1 つによって表されます。

上記の質問に答えるには、次の 2 つの重要な点を考慮する必要があります。

  • セットを解決するにはどうすればよいですか? 最初は、すべての要素が異なるセットに属しています。指定された関係に取り組んだ後、メンバーを選択します。 代表 。代表者を選択するには多くの方法がありますが、簡単な方法は、最大のインデックスを使用して選択することです。
  • 2 人が同じグループに属しているかどうかを確認しますか? 2 人の個人の代表者が同じであれば、彼らは友達になります。

使用されるデータ構造は次のとおりです。

配列: 整数の配列は呼び出されます 親[] 。私たちが扱っているのであれば、 N items では、配列の i 番目の要素が i 番目の項目を表します。より正確には、Parent[] 配列の i 番目の要素は、i 番目の項目の親です。これらの関係により、1 つ以上の仮想ツリーが作成されます。

木: それは 素集合 。 2 つの要素が同じツリー内にある場合、それらは同じツリー内にあります。 素集合 。各ツリーのルート ノード (または最上位ノード) は、 代表 セットの。いつも一つあるよ ユニークな代表者 各セットの。代表者を識別する簡単なルールは、「i」が集合の代表者である場合、 親[i] = i 。 i が彼の集合の代表者でない場合は、代表者が見つかるまでツリーを上っていくことで見つけることができます。

素の集合データ構造に対する操作:

  1. 探す
  2. 連合

1. 以下を見つけます。

それ自体の親であるノードに到達するまで親配列を再帰的に走査することで実装できます。

C++




// Finds the representative of the set> // that i is an element of> > #include> using> namespace> std;> > int> find(> int> i)> > {> > > // If i is the parent of itself> > if> (parent[i] == i) {> > > // Then i is the representative of> > // this set> > return> i;> > }> > else> {> > > // Else if i is not the parent of> > // itself, then i is not the> > // representative of his set. So we> > // recursively call Find on its parent> > return> find(parent[i]);> > }> }> > // The code is contributed by Nidhi goel>

ジャワ




// Finds the representative of the set> // that i is an element of> import> java.io.*;> > class> GFG {> > > static> int> find(> int> i)> > > {> > > // If i is the parent of itself> > if> (parent[i] == i) {> > > // Then i is the representative of> > // this set> > return> i;> > }> > else> {> > > // Else if i is not the parent of> > // itself, then i is not the> > // representative of his set. So we> > // recursively call Find on its parent> > return> find(parent[i]);> > }> > }> }> > // The code is contributed by Nidhi goel>

Python3




# Finds the representative of the set> # that i is an element of> > def> find(i):> > > # If i is the parent of itself> > if> (parent[i]> => => i):> > > # Then i is the representative of> > # this set> > return> i> > else> :> > > # Else if i is not the parent of> > # itself, then i is not the> > # representative of his set. So we> > # recursively call Find on its parent> > return> find(parent[i])> > > # The code is contributed by Nidhi goel>

C#




using> System;> > public> class> GFG{> > > // Finds the representative of the set> > // that i is an element of> > public> static> int> find(> int> i)> > {> > > // If i is the parent of itself> > if> (parent[i] == i) {> > > // Then i is the representative of> > // this set> > return> i;> > }> > else> {> > > // Else if i is not the parent of> > // itself, then i is not the> > // representative of his set. So we> > // recursively call Find on its parent> > return> find(parent[i]);> > }> > }> }>

JavaScript




> // Finds the representative of the set> // that i is an element of> > function> find(i)> {> > > // If i is the parent of itself> > if> (parent[i] == i) {> > > // Then i is the representative of> > // this set> > return> i;> > }> > else> {> > > // Else if i is not the parent of> > // itself, then i is not the> > // representative of his set. So we> > // recursively call Find on its parent> > return> find(parent[i]);> > }> }> // The code is contributed by Nidhi goel> >

時間計算量 : このアプローチは非効率的で、最悪の場合は O(n) 時間がかかる可能性があります。

2. 結合:

かかる 2つの要素 を入力として使用し、そのセットの代表を見つけます。 探す 操作を実行し、最後にいずれかのツリー (セットを表す) をもう一方のツリーのルート ノードの下に置きます。

C++




// Unites the set that includes i> // and the set that includes j> > #include> using> namespace> std;> > void> union> (> int> i,> int> j) {> > > // Find the representatives> > // (or the root nodes) for the set> > // that includes i> > int> irep => this> .Find(i),> > > // And do the same for the set> > // that includes j> > int> jrep => this> .Find(j);> > > // Make the parent of i’s representative> > // be j’s representative effectively> > // moving all of i’s set into j’s set)> > this> .Parent[irep] = jrep;> }>

ジャワ




import> java.util.Arrays;> > public> class> UnionFind {> > private> int> [] parent;> > > public> UnionFind(> int> size) {> > // Initialize the parent array with each element as its own representative> > parent => new> int> [size];> > for> (> int> i => 0> ; i parent[i] = i; } } // Find the representative (root) of the set that includes element i public int find(int i) { if (parent[i] == i) { return i; // i is the representative of its own set } // Recursively find the representative of the parent until reaching the root parent[i] = find(parent[i]); // Path compression return parent[i]; } // Unite (merge) the set that includes element i and the set that includes element j public void union(int i, int j) { int irep = find(i); // Find the representative of set containing i int jrep = find(j); // Find the representative of set containing j // Make the representative of i's set be the representative of j's set parent[irep] = jrep; } public static void main(String[] args) { int size = 5; // Replace with your desired size UnionFind uf = new UnionFind(size); // Perform union operations as needed uf.union(1, 2); uf.union(3, 4); // Check if elements are in the same set boolean inSameSet = uf.find(1) == uf.find(2); System.out.println('Are 1 and 2 in the same set? ' + inSameSet); } }>

Python3




# Unites the set that includes i> # and the set that includes j> > def> union(parent, rank, i, j):> > # Find the representatives> > # (or the root nodes) for the set> > # that includes i> > irep> => find(parent, i)> > > # And do the same for the set> > # that includes j> > jrep> => find(parent, j)> > > # Make the parent of i’s representative> > # be j’s representative effectively> > # moving all of i’s set into j’s set)> > > parent[irep]> => jrep>

C#




using> System;> > public> class> UnionFind> {> > private> int> [] parent;> > > public> UnionFind(> int> size)> > {> > // Initialize the parent array with each element as its own representative> > parent => new> int> [size];> > for> (> int> i = 0; i { parent[i] = i; } } // Find the representative (root) of the set that includes element i public int Find(int i) { if (parent[i] == i) { return i; // i is the representative of its own set } // Recursively find the representative of the parent until reaching the root parent[i] = Find(parent[i]); // Path compression return parent[i]; } // Unite (merge) the set that includes element i and the set that includes element j public void Union(int i, int j) { int irep = Find(i); // Find the representative of set containing i int jrep = Find(j); // Find the representative of set containing j // Make the representative of i's set be the representative of j's set parent[irep] = jrep; } public static void Main() { int size = 5; // Replace with your desired size UnionFind uf = new UnionFind(size); // Perform union operations as needed uf.Union(1, 2); uf.Union(3, 4); // Check if elements are in the same set bool inSameSet = uf.Find(1) == uf.Find(2); Console.WriteLine('Are 1 and 2 in the same set? ' + inSameSet); } }>

JavaScript




// JavaScript code for the approach> > // Unites the set that includes i> // and the set that includes j> function> union(parent, rank, i, j)> {> > // Find the representatives> // (or the root nodes) for the set> // that includes i> let irep = find(parent, i);> > // And do the same for the set> // that includes j> let jrep = find(parent, j);> > // Make the parent of i’s representative> // be j’s representative effectively> // moving all of i’s set into j’s set)> > parent[irep] = jrep;> }>

時間計算量 : このアプローチは非効率的であり、最悪の場合、長さ O(n) のツリーが生成される可能性があります。

最適化 (ランク/サイズによる結合およびパス圧縮):

効率は、どの木が他の木に接続されるかに大きく依存します。 これを行うには 2 つの方法があります。 1 つ目はランクによる結合で、ツリーの高さを係数として考慮します。2 つ目はサイズによる結合で、一方のツリーをもう一方のツリーに接続するときにツリーのサイズを係数として考慮します。この方法とパス圧縮を併用すると、ほぼ一定時間の複雑さが得られます。

パスの圧縮 (Find() の変更):

データ構造を高速化します。 高さを圧縮する 木々の。これは、小さなキャッシュ メカニズムを 探す 手術。詳細については、コードを見てください。

C++




// Finds the representative of the set that i> // is an element of.> > #include> using> namespace> std;> > int> find(> int> i)> {> > > // If i is the parent of itself> > if> (Parent[i] == i) {> > > // Then i is the representative> > return> i;> > }> > else> {> > > // Recursively find the representative.> > int> result = find(Parent[i]);> > > // We cache the result by moving i’s node> > // directly under the representative of this> > // set> > Parent[i] = result;> > > // And then we return the result> > return> result;> > }> }>

ジャワ




// Finds the representative of the set that i> // is an element of.> import> java.io.*;> import> java.util.*;> > static> int> find(> int> i)> {> > > // If i is the parent of itself> > if> (Parent[i] == i) {> > > // Then i is the representative> > return> i;> > }> > else> {> > > // Recursively find the representative.> > int> result = find(Parent[i]);> > > // We cache the result by moving i’s node> > // directly under the representative of this> > // set> > Parent[i] = result;> > > // And then we return the result> > return> result;> > }> }> > // The code is contributed by Arushi jindal.>

Python3




# Finds the representative of the set that i> # is an element of.> > > def> find(i):> > > # If i is the parent of itself> > if> Parent[i]> => => i:> > > # Then i is the representative> > return> i> > else> :> > > # Recursively find the representative.> > result> => find(Parent[i])> > > # We cache the result by moving i’s node> > # directly under the representative of this> > # set> > Parent[i]> => result> > > # And then we return the result> > return> result> > # The code is contributed by Arushi Jindal.>

C#




using> System;> > // Finds the representative of the set that i> // is an element of.> public> static> int> find(> int> i)> {> > > // If i is the parent of itself> > if> (Parent[i] == i) {> > > // Then i is the representative> > return> i;> > }> > else> {> > > // Recursively find the representative.> > int> result = find(Parent[i]);> > > // We cache the result by moving i’s node> > // directly under the representative of this> > // set> > Parent[i] = result;> > > // And then we return the result> > return> result;> > }> }> > // The code is contributed by Arushi Jindal.>

JavaScript




// Finds the representative of the set that i> // is an element of.> > > function> find(i)> {> > > // If i is the parent of itself> > if> (Parent[i] == i) {> > > // Then i is the representative> > return> i;> > }> > else> {> > > // Recursively find the representative.> > let result = find(Parent[i]);> > > // We cache the result by moving i’s node> > // directly under the representative of this> > // set> > Parent[i] = result;> > > // And then we return the result> > return> result;> > }> }> > // The code is contributed by Arushi Jindal.>

時間計算量 : 呼び出しごとに平均 O(log n)。

ランクによる結合 :

まず最初に、という名前の新しい整数配列が必要です。 ランク[] 。この配列のサイズは親配列と同じです 親[] 。私が集合の代表である場合、 ランク[i] セットを表す木の高さです。
ここで、Union 操作では、2 つのツリーのどちらがもう一方の下に移動されるかは問題ではないことを思い出してください。ここで私たちがやりたいことは、結果として得られる木の高さを最小限に抑えることです。 2 つのツリー (またはセット) を結合する場合、それらを左と右と呼びましょう。すべては 左のランク そしてその 右のランク

  • のランクであれば、 のランク未満です なら移動するのが一番いいよ 左下右 なぜなら、それによって右の順位は変わらないからです(右下を左に移動すると高さが上がります)。同様に、右の順位が左の順位よりも低い場合は、右の下に移動する必要があります。
  • ランクが等しい場合、どのツリーが他のツリーの下にあるかは問題ではありませんが、結果のランクは常にツリーのランクより 1 つ大きくなります。

C++




// Unites the set that includes i and the set> // that includes j by rank> > #include> using> namespace> std;> > void> unionbyrank(> int> i,> int> j) {> > > // Find the representatives (or the root nodes)> > // for the set that includes i> > int> irep => this> .find(i);> > > // And do the same for the set that includes j> > int> jrep => this> .Find(j);> > > // Elements are in same set, no need to> > // unite anything.> > if> (irep == jrep)> > return> ;> > > // Get the rank of i’s tree> > irank = Rank[irep],> > > // Get the rank of j’s tree> > jrank = Rank[jrep];> > > // If i’s rank is less than j’s rank> > if> (irank // Then move i under j this.parent[irep] = jrep; } // Else if j’s rank is less than i’s rank else if (jrank // Then move j under i this.Parent[jrep] = irep; } // Else if their ranks are the same else { // Then move i under j (doesn’t matter // which one goes where) this.Parent[irep] = jrep; // And increment the result tree’s // rank by 1 Rank[jrep]++; } }>

ジャワ




public> class> DisjointSet {> > > private> int> [] parent;> > private> int> [] rank;> > > // Constructor to initialize the DisjointSet data> > // structure> > public> DisjointSet(> int> size)> > {> > parent => new> int> [size];> > rank => new> int> [size];> > > // Initialize each element as a separate set with> > // rank 0> > for> (> int> i => 0> ; i parent[i] = i; rank[i] = 0; } } // Function to find the representative (or the root // node) of a set with path compression private int find(int i) { if (parent[i] != i) { parent[i] = find(parent[i]); // Path compression } return parent[i]; } // Unites the set that includes i and the set that // includes j by rank public void unionByRank(int i, int j) { // Find the representatives (or the root nodes) for // the set that includes i and j int irep = find(i); int jrep = find(j); // Elements are in the same set, no need to unite // anything if (irep == jrep) { return; } // Get the rank of i's tree int irank = rank[irep]; // Get the rank of j's tree int jrank = rank[jrep]; // If i's rank is less than j's rank if (irank // Move i under j parent[irep] = jrep; } // Else if j's rank is less than i's rank else if (jrank // Move j under i parent[jrep] = irep; } // Else if their ranks are the same else { // Move i under j (doesn't matter which one goes // where) parent[irep] = jrep; // Increment the result tree's rank by 1 rank[jrep]++; } } // Example usage public static void main(String[] args) { int size = 5; DisjointSet ds = new DisjointSet(size); // Perform some union operations ds.unionByRank(0, 1); ds.unionByRank(2, 3); ds.unionByRank(1, 3); // Find the representative of each element and print // the result for (int i = 0; i System.out.println( 'Element ' + i + ' belongs to the set with representative ' + ds.find(i)); } } }>

Python3




class> DisjointSet:> > def> __init__(> self> , size):> > self> .parent> => [i> for> i> in> range> (size)]> > self> .rank> => [> 0> ]> *> size> > > # Function to find the representative (or the root node) of a set> > def> find(> self> , i):> > # If i is not the representative of its set, recursively find the representative> > if> self> .parent[i] !> => i:> > self> .parent[i]> => self> .find(> self> .parent[i])> # Path compression> > return> self> .parent[i]> > > # Unites the set that includes i and the set that includes j by rank> > def> union_by_rank(> self> , i, j):> > # Find the representatives (or the root nodes) for the set that includes i and j> > irep> => self> .find(i)> > jrep> => self> .find(j)> > > # Elements are in the same set, no need to unite anything> > if> irep> => => jrep:> > return> > > # Get the rank of i's tree> > irank> => self> .rank[irep]> > > # Get the rank of j's tree> > jrank> => self> .rank[jrep]> > > # If i's rank is less than j's rank> > if> irank # Move i under j self.parent[irep] = jrep # Else if j's rank is less than i's rank elif jrank # Move j under i self.parent[jrep] = irep # Else if their ranks are the same else: # Move i under j (doesn't matter which one goes where) self.parent[irep] = jrep # Increment the result tree's rank by 1 self.rank[jrep] += 1 def main(self): # Example usage size = 5 ds = DisjointSet(size) # Perform some union operations ds.union_by_rank(0, 1) ds.union_by_rank(2, 3) ds.union_by_rank(1, 3) # Find the representative of each element for i in range(size): print(f'Element {i} belongs to the set with representative {ds.find(i)}') # Creating an instance and calling the main method ds = DisjointSet(size=5) ds.main()>

C#




using> System;> > class> DisjointSet {> > private> int> [] parent;> > private> int> [] rank;> > > public> DisjointSet(> int> size) {> > parent => new> int> [size];> > rank => new> int> [size];> > > // Initialize each element as a separate set> > for> (> int> i = 0; i parent[i] = i; rank[i] = 0; } } // Function to find the representative (or the root node) of a set private int Find(int i) { // If i is not the representative of its set, recursively find the representative if (parent[i] != i) { parent[i] = Find(parent[i]); // Path compression } return parent[i]; } // Unites the set that includes i and the set that includes j by rank public void UnionByRank(int i, int j) { // Find the representatives (or the root nodes) for the set that includes i and j int irep = Find(i); int jrep = Find(j); // Elements are in the same set, no need to unite anything if (irep == jrep) { return; } // Get the rank of i's tree int irank = rank[irep]; // Get the rank of j's tree int jrank = rank[jrep]; // If i's rank is less than j's rank if (irank // Move i under j parent[irep] = jrep; } // Else if j's rank is less than i's rank else if (jrank // Move j under i parent[jrep] = irep; } // Else if their ranks are the same else { // Move i under j (doesn't matter which one goes where) parent[irep] = jrep; // Increment the result tree's rank by 1 rank[jrep]++; } } static void Main() { // Example usage int size = 5; DisjointSet ds = new DisjointSet(size); // Perform some union operations ds.UnionByRank(0, 1); ds.UnionByRank(2, 3); ds.UnionByRank(1, 3); // Find the representative of each element for (int i = 0; i Console.WriteLine('Element ' + i + ' belongs to the set with representative ' + ds.Find(i)); } } }>

JavaScript




// JavaScript Program for the above approach> unionbyrank(i, j) {> let irep => this> .find(i);> // Find representative of set including i> let jrep => this> .find(j);> // Find representative of set including j> > if> (irep === jrep) {> return> ;> // Elements are already in the same set> }> > let irank => this> .rank[irep];> // Rank of set including i> let jrank => this> .rank[jrep];> // Rank of set including j> > if> (irank this.parent[irep] = jrep; // Make j's representative parent of i's representative } else if (jrank this.parent[jrep] = irep; // Make i's representative parent of j's representative } else { this.parent[irep] = jrep; // Make j's representative parent of i's representative this.rank[jrep]++; // Increment the rank of the resulting set }>

サイズによる結合:

繰り返しますが、という新しい整数配列が必要です。 サイズ[] 。この配列のサイズは親配列と同じです 親[] 。私が集合の代表である場合、 サイズ[i] セットを表すツリー内の要素の数です。
ここで 2 つのツリー (またはセット) を結合しています。それらを左と右と呼びましょう。この場合、すべては 左のサイズ そしてその 右のサイズ ツリー(またはセット)。

  • サイズが のサイズより小さいです 、それなら移動するのが一番です 左下右 そして、右側のサイズを左側のサイズだけ大きくします。同様に、右のサイズが左のサイズより小さい場合は、右を左の下に移動する必要があります。左側のサイズを右側のサイズだけ大きくします。
  • サイズが等しい場合、どちらの木がもう一方の下に来るかは問題ではありません。

C++




// Unites the set that includes i and the set> // that includes j by size> > #include> using> namespace> std;> > void> unionBySize(> int> i,> int> j) {> > > // Find the representatives (or the root nodes)> > // for the set that includes i> > int> irep = find(i);> > > // And do the same for the set that includes j> > int> jrep = find(j);> > > // Elements are in the same set, no need to> > // unite anything.> > if> (irep == jrep)> > return> ;> > > // Get the size of i’s tree> > int> isize = Size[irep];> > > // Get the size of j’s tree> > int> jsize = Size[jrep];> > > // If i’s size is less than j’s size> > if> (isize // Then move i under j Parent[irep] = jrep; // Increment j's size by i's size Size[jrep] += Size[irep]; } // Else if j’s size is less than i’s size else { // Then move j under i Parent[jrep] = irep; // Increment i's size by j's size Size[irep] += Size[jrep]; } }>

ジャワ




// Java program for the above approach> import> java.util.Arrays;> > class> UnionFind {> > > private> int> [] Parent;> > private> int> [] Size;> > > public> UnionFind(> int> n)> > {> > // Initialize Parent array> > Parent => new> int> [n];> > for> (> int> i => 0> ; i Parent[i] = i; } // Initialize Size array with 1s Size = new int[n]; Arrays.fill(Size, 1); } // Function to find the representative (or the root // node) for the set that includes i public int find(int i) { if (Parent[i] != i) { // Path compression: Make the parent of i the // root of the set Parent[i] = find(Parent[i]); } return Parent[i]; } // Unites the set that includes i and the set that // includes j by size public void unionBySize(int i, int j) { // Find the representatives (or the root nodes) for // the set that includes i int irep = find(i); // And do the same for the set that includes j int jrep = find(j); // Elements are in the same set, no need to unite // anything. if (irep == jrep) return; // Get the size of i’s tree int isize = Size[irep]; // Get the size of j’s tree int jsize = Size[jrep]; // If i’s size is less than j’s size if (isize // Then move i under j Parent[irep] = jrep; // Increment j's size by i's size Size[jrep] += Size[irep]; } // Else if j’s size is less than i’s size else { // Then move j under i Parent[jrep] = irep; // Increment i's size by j's size Size[irep] += Size[jrep]; } } } public class GFG { public static void main(String[] args) { // Example usage int n = 5; UnionFind unionFind = new UnionFind(n); // Perform union operations unionFind.unionBySize(0, 1); unionFind.unionBySize(2, 3); unionFind.unionBySize(0, 4); // Print the representative of each element after // unions for (int i = 0; i System.out.println('Element ' + i + ': Representative = ' + unionFind.find(i)); } } } // This code is contributed by Susobhan Akhuli>

Python3




# Python program for the above approach> class> UnionFind:> > def> __init__(> self> , n):> > # Initialize Parent array> > self> .Parent> => list> (> range> (n))> > > # Initialize Size array with 1s> > self> .Size> => [> 1> ]> *> n> > > # Function to find the representative (or the root node) for the set that includes i> > def> find(> self> , i):> > if> self> .Parent[i] !> => i:> > # Path compression: Make the parent of i the root of the set> > self> .Parent[i]> => self> .find(> self> .Parent[i])> > return> self> .Parent[i]> > > # Unites the set that includes i and the set that includes j by size> > def> unionBySize(> self> , i, j):> > # Find the representatives (or the root nodes) for the set that includes i> > irep> => self> .find(i)> > > # And do the same for the set that includes j> > jrep> => self> .find(j)> > > # Elements are in the same set, no need to unite anything.> > if> irep> => => jrep:> > return> > > # Get the size of i’s tree> > isize> => self> .Size[irep]> > > # Get the size of j’s tree> > jsize> => self> .Size[jrep]> > > # If i’s size is less than j’s size> > if> isize # Then move i under j self.Parent[irep] = jrep # Increment j's size by i's size self.Size[jrep] += self.Size[irep] # Else if j’s size is less than i’s size else: # Then move j under i self.Parent[jrep] = irep # Increment i's size by j's size self.Size[irep] += self.Size[jrep] # Example usage n = 5 unionFind = UnionFind(n) # Perform union operations unionFind.unionBySize(0, 1) unionFind.unionBySize(2, 3) unionFind.unionBySize(0, 4) # Print the representative of each element after unions for i in range(n): print('Element {}: Representative = {}'.format(i, unionFind.find(i))) # This code is contributed by Susobhan Akhuli>

C#




using> System;> > class> UnionFind> {> > private> int> [] Parent;> > private> int> [] Size;> > > public> UnionFind(> int> n)> > {> > // Initialize Parent array> > Parent => new> int> [n];> > for> (> int> i = 0; i { Parent[i] = i; } // Initialize Size array with 1s Size = new int[n]; for (int i = 0; i { Size[i] = 1; } } // Function to find the representative (or the root node) for the set that includes i public int Find(int i) { if (Parent[i] != i) { // Path compression: Make the parent of i the root of the set Parent[i] = Find(Parent[i]); } return Parent[i]; } // Unites the set that includes i and the set that includes j by size public void UnionBySize(int i, int j) { // Find the representatives (or the root nodes) for the set that includes i int irep = Find(i); // And do the same for the set that includes j int jrep = Find(j); // Elements are in the same set, no need to unite anything. if (irep == jrep) return; // Get the size of i’s tree int isize = Size[irep]; // Get the size of j’s tree int jsize = Size[jrep]; // If i’s size is less than j’s size if (isize { // Then move i under j Parent[irep] = jrep; // Increment j's size by i's size Size[jrep] += Size[irep]; } // Else if j’s size is less than i’s size else { // Then move j under i Parent[jrep] = irep; // Increment i's size by j's size Size[irep] += Size[jrep]; } } } class Program { static void Main() { // Example usage int n = 5; UnionFind unionFind = new UnionFind(n); // Perform union operations unionFind.UnionBySize(0, 1); unionFind.UnionBySize(2, 3); unionFind.UnionBySize(0, 4); // Print the representative of each element after unions for (int i = 0; i { Console.WriteLine($'Element {i}: Representative = {unionFind.Find(i)}'); } } }>

JavaScript




unionbysize(i, j) {> > let irep => this> .find(i);> // Find the representative of the set containing i.> > let jrep => this> .find(j);> // Find the representative of the set containing j.> > > if> (irep === jrep) {> > return> ;> // Elements are already in the same set.> > }> > > let isize => this> .size[irep];> // Size of the set including i.> > let jsize => this> .size[jrep];> // Size of the set including j.> > > if> (isize // If i's size is less than j's size, make i's representative // a child of j's representative. this.parent[irep] = jrep; this.size[jrep] += this.size[irep]; // Increment j's size by i's size. } else { // If j's size is less than or equal to i's size, make j's representative // a child of i's representative. this.parent[jrep] = irep; this.size[irep] += this.size[jrep]; // Increment i's size by j's size. if (isize === jsize) { // If sizes are equal, increment the rank of i's representative. this.rank[irep]++; } } }>

出力

Element 0: Representative = 0 Element 1: Representative = 0 Element 2: Representative = 2 Element 3: Representative = 2 Element 4: Representative = 0 

時間計算量 : パス圧縮なしの場合は O(log n)。

以下は、パス圧縮とランクによる結合を備えた素セットの完全な実装です。

C++




// C++ implementation of disjoint set> > #include> using> namespace> std;> > class> DisjSet {> > int> *rank, *parent, n;> > public> :> > > // Constructor to create and> > // initialize sets of n items> > DisjSet(> int> n)> > {> > rank => new> int> [n];> > parent => new> int> [n];> > this> ->n = n;>> > makeSet();> > }> > > // Creates n single item sets> > void> makeSet()> > {> > for> (> int> i = 0; i parent[i] = i; } } // Finds set of given item x int find(int x) { // Finds the representative of the set // that x is an element of if (parent[x] != x) { // if x is not the parent of itself // Then x is not the representative of // his set, parent[x] = find(parent[x]); // so we recursively call Find on its parent // and move i's node directly under the // representative of this set } return parent[x]; } // Do union of two sets by rank represented // by x and y. void Union(int x, int y) { // Find current sets of x and y int xset = find(x); int yset = find(y); // If they are already in same set if (xset == yset) return; // Put smaller ranked item under // bigger ranked item if ranks are // different if (rank[xset] parent[xset] = yset; } else if (rank[xset]>ランク[yset]) { 親[yset] = xset; } // ランクが同じ場合、 // ランクをインクリメントします。 else { 親[yset] = xset; ランク[xset] = ランク[xset] + 1; } } }; // ドライバーコード int main() { // 関数呼び出し DisjSet obj(5); obj.Union(0, 2); obj.Union(4, 2); obj.Union(3, 1); if (obj.find(4) == obj.find(0)) cout < < 'Yes '; else cout < < 'No '; if (obj.find(1) == obj.find(0)) cout < < 'Yes '; else cout < < 'No '; return 0; }>

ジャワ




// A Java program to implement Disjoint Set Data> // Structure.> import> java.io.*;> import> java.util.*;> > class> DisjointUnionSets {> > int> [] rank, parent;> > int> n;> > > // Constructor> > public> DisjointUnionSets(> int> n)> > {> > rank => new> int> [n];> > parent => new> int> [n];> > this> .n = n;> > makeSet();> > }> > > // Creates n sets with single item in each> > void> makeSet()> > {> > for> (> int> i => 0> ; i // Initially, all elements are in // their own set. parent[i] = i; } } // Returns representative of x's set int find(int x) { // Finds the representative of the set // that x is an element of if (parent[x] != x) { // if x is not the parent of itself // Then x is not the representative of // his set, parent[x] = find(parent[x]); // so we recursively call Find on its parent // and move i's node directly under the // representative of this set } return parent[x]; } // Unites the set that includes x and the set // that includes x void union(int x, int y) { // Find representatives of two sets int xRoot = find(x), yRoot = find(y); // Elements are in the same set, no need // to unite anything. if (xRoot == yRoot) return; // If x's rank is less than y's rank if (rank[xRoot] // Then move x under y so that depth // of tree remains less parent[xRoot] = yRoot; // Else if y's rank is less than x's rank else if (rank[yRoot] // Then move y under x so that depth of // tree remains less parent[yRoot] = xRoot; else // if ranks are the same { // Then move y under x (doesn't matter // which one goes where) parent[yRoot] = xRoot; // And increment the result tree's // rank by 1 rank[xRoot] = rank[xRoot] + 1; } } } // Driver code public class Main { public static void main(String[] args) { // Let there be 5 persons with ids as // 0, 1, 2, 3 and 4 int n = 5; DisjointUnionSets dus = new DisjointUnionSets(n); // 0 is a friend of 2 dus.union(0, 2); // 4 is a friend of 2 dus.union(4, 2); // 3 is a friend of 1 dus.union(3, 1); // Check if 4 is a friend of 0 if (dus.find(4) == dus.find(0)) System.out.println('Yes'); else System.out.println('No'); // Check if 1 is a friend of 0 if (dus.find(1) == dus.find(0)) System.out.println('Yes'); else System.out.println('No'); } }>

Python3




# Python3 program to implement Disjoint Set Data> # Structure.> > class> DisjSet:> > def> __init__(> self> , n):> > # Constructor to create and> > # initialize sets of n items> > self> .rank> => [> 1> ]> *> n> > self> .parent> => [i> for> i> in> range> (n)]> > > > # Finds set of given item x> > def> find(> self> , x):> > > # Finds the representative of the set> > # that x is an element of> > if> (> self> .parent[x] !> => x):> > > # if x is not the parent of itself> > # Then x is not the representative of> > # its set,> > self> .parent[x]> => self> .find(> self> .parent[x])> > > # so we recursively call Find on its parent> > # and move i's node directly under the> > # representative of this set> > > return> self> .parent[x]> > > > # Do union of two sets represented> > # by x and y.> > def> Union(> self> , x, y):> > > # Find current sets of x and y> > xset> => self> .find(x)> > yset> => self> .find(y)> > > # If they are already in same set> > if> xset> => => yset:> > return> > > # Put smaller ranked item under> > # bigger ranked item if ranks are> > # different> > if> self> .rank[xset] <> self> .rank[yset]:> > self> .parent[xset]> => yset> > > elif> self> .rank[xset]>>> self> .rank[yset]:> > self> .parent[yset]> => xset> > > # If ranks are same, then move y under> > # x (doesn't matter which one goes where)> > # and increment rank of x's tree> > else> :> > self> .parent[yset]> => xset> > self> .rank[xset]> => self> .rank[xset]> +> 1> > # Driver code> obj> => DisjSet(> 5> )> obj.Union(> 0> ,> 2> )> obj.Union(> 4> ,> 2> )> obj.Union(> 3> ,> 1> )> if> obj.find(> 4> )> => => obj.find(> 0> ):> > print> (> 'Yes'> )> else> :> > print> (> 'No'> )> if> obj.find(> 1> )> => => obj.find(> 0> ):> > print> (> 'Yes'> )> else> :> > print> (> 'No'> )> > # This code is contributed by ng24_7.>

C#




// A C# program to implement> // Disjoint Set Data Structure.> using> System;> > class> DisjointUnionSets> {> > int> [] rank, parent;> > int> n;> > > // Constructor> > public> DisjointUnionSets(> int> n)> > {> > rank => new> int> [n];> > parent => new> int> [n];> > this> .n = n;> > makeSet();> > }> > > // Creates n sets with single item in each> > public> void> makeSet()> > {> > for> (> int> i = 0; i { // Initially, all elements are in // their own set. parent[i] = i; } } // Returns representative of x's set public int find(int x) { // Finds the representative of the set // that x is an element of if (parent[x] != x) { // if x is not the parent of itself // Then x is not the representative of // his set, parent[x] = find(parent[x]); // so we recursively call Find on its parent // and move i's node directly under the // representative of this set } return parent[x]; } // Unites the set that includes x and // the set that includes x public void union(int x, int y) { // Find representatives of two sets int xRoot = find(x), yRoot = find(y); // Elements are in the same set, // no need to unite anything. if (xRoot == yRoot) return; // If x's rank is less than y's rank if (rank[xRoot] // Then move x under y so that depth // of tree remains less parent[xRoot] = yRoot; // Else if y's rank is less than x's rank else if (rank[yRoot] // Then move y under x so that depth of // tree remains less parent[yRoot] = xRoot; else // if ranks are the same { // Then move y under x (doesn't matter // which one goes where) parent[yRoot] = xRoot; // And increment the result tree's // rank by 1 rank[xRoot] = rank[xRoot] + 1; } } } // Driver code class GFG { public static void Main(String[] args) { // Let there be 5 persons with ids as // 0, 1, 2, 3 and 4 int n = 5; DisjointUnionSets dus = new DisjointUnionSets(n); // 0 is a friend of 2 dus.union(0, 2); // 4 is a friend of 2 dus.union(4, 2); // 3 is a friend of 1 dus.union(3, 1); // Check if 4 is a friend of 0 if (dus.find(4) == dus.find(0)) Console.WriteLine('Yes'); else Console.WriteLine('No'); // Check if 1 is a friend of 0 if (dus.find(1) == dus.find(0)) Console.WriteLine('Yes'); else Console.WriteLine('No'); } } // This code is contributed by Rajput-Ji>

JavaScript




class DisjSet {> > constructor(n) {> > this> .rank => new> Array(n);> > this> .parent => new> Array(n);> > this> .n = n;> > this> .makeSet();> > }> > > makeSet() {> > for> (let i = 0; i <> this> .n; i++) {> > this> .parent[i] = i;> > }> > }> > > find(x) {> > if> (> this> .parent[x] !== x) {> > this> .parent[x] => this> .find(> this> .parent[x]);> > }> > return> this> .parent[x];> > }> > > Union(x, y) {> > let xset => this> .find(x);> > let yset => this> .find(y);> > > if> (xset === yset)> return> ;> > > if> (> this> .rank[xset] <> this> .rank[yset]) {> > this> .parent[xset] = yset;> > }> else> if> (> this> .rank[xset]>>> this> .rank[yset]) {> > this> .parent[yset] = xset;> > }> else> {> > this> .parent[yset] = xset;> > this> .rank[xset] => this> .rank[xset] + 1;> > }> > }> }> > // usage example> let obj => new> DisjSet(5);> obj.Union(0, 2);> obj.Union(4, 2);> obj.Union(3, 1);> > if> (obj.find(4) === obj.find(0)) {> > console.log(> 'Yes'> );> }> else> {> > console.log(> 'No'> );> }> if> (obj.find(1) === obj.find(0)) {> > console.log(> 'Yes'> );> }> else> {> > console.log(> 'No'> );> }>

出力

Yes No 

時間計算量 : n 個の単一項目セットを作成するには O(n)。 2 つの手法 - ランク/サイズによる結合を使用したパス圧縮では、時間計算量はほぼ一定時間に達します。結局のところ、決勝は 償却時間計算量 は O(α(n)) です。ここで、α(n) は逆アッカーマン関数であり、非常に着実に増加します (n <10 の場合でも超えません) 600 約)。

空間の複雑さ: O(n) は、Disjoint Set データ構造に n 個の要素を格納する必要があるためです。



トップ記事

カテゴリ

興味深い記事