서로소 집합(Disjoint Set) : 공통 원소가 없는 두 집합

서로소 부분 집합들로 나누어진 원소들을 데이터를 처리하기 위한 자료구조이다.

union과 find 2개의 연사으로 조작할 수 있다.

트리 자료구조로 표현한다.

 

✔️union-find 방식

1. union(합집합)

   1) A와 B의 부모노드 A', B'을 각각 찾는다.

   2) A'를 B'의 부모 노드로 설정한다. (if. A' < B') 

 

2. find(부모노드 찾기)

    루트 노드 탐색

 

<1번째 소스 : 수정 전>

#include <iostream>
#include <vector>

using namespace std;

// 노드의 개수(v)와 간선(e)의 개수
// 노드의 개수는 최대 100000개라고 가정
int v, e;
int parent[100001];

// 특정 원소가 속한 집합을 찾기
int findParent(int x) {
    if (parent[x] != x) {
        return findParent(parent[x]);
    }
    return x;
}

// 두 원소가 속한 집합을 합치기
void unionParent(int x, int y) {
    x = findParent(x);
    y = findParent(y);

    if (x < y) {
        parent[y] = x;
    }
    else {
        parent[x] = y;
    }
}

int main(void) {

    cin >> v >> e;

    // 부모테이블 상에서, 부모를 자기 자신으로 초기화
    for (int i = 1; i <= v; i++) {
        parent[i] = i;
    }

    // union 연산을 각각 수행
    for (int i = 0; i < e; i++) {
        int a, b;
        cin >> a >> b;
        unionParent(a, b);
    }

    // 각 원소가 속한 집할 출력
    cout << "각 원소가 속한 집합: ";
    for (int i = 1 ; i <= v; i++) {
        cout << findParent(i) << " ";
    }
    cout << endl;

    // 부모 테이블 내용 출력
    cout << "부모 테이블: ";
    for (int i = 1 ; i <= v; i++) {
        cout << parent[i] << " ";
    }
    cout << endl;

    return 0 ;
}

 

위의 소스는 노드의 개수(V)이고 union연산의 개수(M)일때, 시간복잡도가 O(VM)이다.

따라서 findParent 함수 부분을 다음과 같이 변경해준다.

int findParent(int x) {
    if (parent[x] != x) {
        parent[x] = findParent(parent[x]);
    }
    return parent[x];
}

수정 후에는 시간복잡도가    

이다.

해당 부분의 시간복잡도 증명은 다음에 다루겠다

즉, 최종 소스는 다음과 같다.

<2번째 소스 : 수정 후>

#include <iostream>
#include <vector>

using namespace std;

// 노드의 개수(v)와 간선(e)의 개수
// 노드의 개수는 최대 100000개라고 가정
int v, e;
int parent[100001];

// 특정 원소가 속한 집합을 찾기
int findParent(int x) {
     if (parent[x] != x) {
         parent[x] = findParent(parent[x]);
     }
     return parent[x];
 }

// 두 원소가 속한 집합을 합치기
void unionParent(int x, int y) {
    x = findParent(x);
    y = findParent(y);

    if (x < y) {
        parent[y] = x;
    }
    else {
        parent[x] = y;
    }
}

int main(void) {

    cin >> v >> e;

    // 부모테이블 상에서, 부모를 자기 자신으로 초기화
    for (int i = 1; i <= v; i++) {
        parent[i] = i;
    }

    // union 연산을 각각 수행
    for (int i = 0; i < e; i++) {
        int a, b;
        cin >> a >> b;
        unionParent(a, b);
    }

    // 각 원소가 속한 집할 출력
    cout << "각 원소가 속한 집합: ";
    for (int i = 1 ; i <= v; i++) {
        cout << findParent(i) << " ";
    }
    cout << endl;

    // 부모 테이블 내용 출력
    cout << "부모 테이블: ";
    for (int i = 1 ; i <= v; i++) {
        cout << parent[i] << " ";
    }
    cout << endl;

    return 0 ;
}

+ Recent posts