서로소 집합(Union-Find 알고리즘) 소개

Disjoint set 데이터 구조란 무엇입니까?

두 세트가 호출됩니다. 서로소 집합 공통 요소가 없으면 세트의 교집합은 널 세트입니다.

서로 겹치지 않거나 분리된 요소 하위 집합을 저장하는 데이터 구조를 분리된 집합 데이터 구조라고 합니다. 서로소 집합 데이터 구조는 다음 작업을 지원합니다.

  • 서로소 집합에 새 집합을 추가합니다.
  • 다음을 사용하여 서로소 집합을 단일 서로소 집합으로 병합 노동 조합 작업.
  • 다음을 사용하여 서로소 집합의 대표 찾기 찾다 작업.
  • 두 집합이 서로소인지 아닌지 확인하세요.

여러 사람이 있고 그들에게 다음 작업을 수행해야 하는 상황을 고려하십시오.

  • 을 추가하다 새로운 우정 관계 , 즉 사람 x가 다른 사람 y의 친구가 됩니다. 즉 집합에 새 요소를 추가합니다.
  • 개인인지 알아보세요. x는 y개인의 친구이다 (직접적이든 간접적이든 친구)

예:

a, b, c, d, e, f, g, h, i, j라고 말하는 10명의 개인이 있습니다.

추가할 관계는 다음과 같습니다.
a b
ㄴ디
cf
c 나는
제이
gj

a가 d의 친구인지 아닌지와 같은 쿼리가 주어졌습니다. 기본적으로 다음과 같은 4개의 그룹을 만들고 그룹 항목 간에 빠르게 액세스할 수 있는 연결을 유지해야 합니다.
G1 = {a, b, d}
G2 = {c, f, i}
G3 = {e,g,j}
G4 = {h}

x와 y가 같은 그룹에 속하는지 여부를 확인합니다. 즉, x와 y가 직접/간접 친구인지 확인합니다.

개인을 그들이 속하는 그룹에 따라 다른 세트로 분할합니다. 이 방법은 다음과 같이 알려져 있습니다. 서로소 집합 합집합 컬렉션을 유지 관리하는 서로소 집합 각 집합은 해당 구성원 중 하나로 표시됩니다.

위의 질문에 답하기 위해 고려해야 할 두 가지 핵심 사항은 다음과 같습니다.

  • 세트를 해결하는 방법은 무엇입니까? 처음에는 모든 요소가 서로 다른 세트에 속합니다. 주어진 관계에 대한 작업을 마친 후 멤버를 선택합니다. 대표 . 대표자를 선택하는 방법에는 여러 가지가 있을 수 있는데, 가장 간단한 방법은 가장 큰 지수로 선택하는 것입니다.
  • 같은 그룹에 2명이 있는지 확인하세요. 두 개인의 대표자가 동일하면 친구가 됩니다.

사용되는 데이터 구조는 다음과 같습니다.

정렬: 정수 배열이 호출됩니다. 부모의[] . 우리가 다루고 있는 경우 N 항목, 배열의 i번째 요소는 i번째 항목을 나타냅니다. 보다 정확하게는 Parent[] 배열의 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>

파이썬3




# 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>

씨#




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]);> > }> > }> }>

자바스크립트




> // 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. 연합:

소요됩니다 두 가지 요소 입력으로 사용하고 다음을 사용하여 집합의 대표자를 찾습니다. 찾다 작업을 수행하고 마지막으로 트리 중 하나(집합을 나타냄)를 다른 트리의 루트 노드 아래에 놓습니다.

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); } }>

파이썬3




# 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>

씨#




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 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가지가 있습니다. 첫 번째는 나무의 높이를 요소로 고려하는 Union by Rank이고, 두 번째는 나무의 크기를 요소로 고려하여 한 나무를 다른 나무에 연결하는 Union by Size입니다. 경로 압축과 함께 이 방법은 거의 일정한 시간의 복잡성을 제공합니다.

경로 압축 (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.>

파이썬3




# 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.>

씨#




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.>

자바스크립트




// 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 작업에서 두 ​​트리 중 어느 트리가 다른 트리 아래로 이동되는지는 중요하지 않습니다. 이제 우리가 원하는 것은 결과 트리의 높이를 최소화하는 것입니다. 두 개의 트리(또는 세트)를 결합하는 경우 이를 왼쪽과 오른쪽이라고 부르면 모든 것이 다음에 따라 달라집니다. 왼쪽 순위 그리고 오른쪽 순위 .

  • 만약 순위가 왼쪽 의 순위보다 낮습니다. 오른쪽 , 그렇다면 이동하는 것이 가장 좋습니다 왼쪽 아래 오른쪽 , 오른쪽 순위는 변경되지 않기 때문입니다(왼쪽 아래 오른쪽으로 이동하면 높이가 증가합니다). 마찬가지로 오른쪽 순위가 왼쪽 순위보다 낮으면 오른쪽 아래 왼쪽으로 이동해야 합니다.
  • 순위가 동일하면 어떤 트리가 다른 트리 아래에 있는지는 중요하지 않지만 결과의 순위는 항상 트리의 순위보다 하나 더 높습니다.

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)); } } }>

파이썬3




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()>

씨#




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 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] 집합을 나타내는 트리의 요소 수입니다.
이제 우리는 두 개의 트리(또는 세트)를 결합하고 있습니다. 이를 왼쪽과 오른쪽이라고 부르겠습니다. 이 경우에는 모두 다음에 따라 달라집니다. 왼쪽의 크기 그리고 오른쪽의 크기 나무(또는 세트).

  • 크기의 경우 왼쪽 의 크기보다 작습니다. 오른쪽 , 그렇다면 이동하는 것이 가장 좋습니다 왼쪽 아래 오른쪽 왼쪽 크기만큼 오른쪽 크기를 늘립니다. 같은 방식으로 오른쪽의 크기가 왼쪽의 크기보다 작으면 오른쪽 아래로 이동해야 합니다. 왼쪽 크기를 오른쪽 크기만큼 늘립니다.
  • 크기가 동일하면 어느 트리가 다른 트리 아래에 있는지는 중요하지 않습니다.

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>

파이썬3




# 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>

씨#




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)}'); } } }>

자바스크립트




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'); } }>

파이썬3




# 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.>

씨#




// 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>

자바스크립트




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)입니다. 두 가지 기술 - 순위/크기별 결합을 통한 경로 압축, 시간 복잡도는 거의 일정한 시간에 도달합니다. 밝혀진 바에 따르면, 결승전은 상각된 시간 복잡도 O(α(n))입니다. 여기서 α(n)은 역 Ackermann 함수로, 매우 꾸준히 증가합니다(n <10인 경우에도 초과하지 않음). 600 약).

공간 복잡도: O(n)은 Disjoint Set 데이터 구조에 n개의 요소를 저장해야 하기 때문입니다.