728x90

분리집합 자료구조는 그래프의 형태로 보이는 문제들에서 유용하게 쓰인다.

1) 두 정점을 연결하는 함수 (Union)

2) 두 정점이 연결되어 있는지를 확인하는 함수 (Find)

가 있다.

대표적으로 Minimum Spanning Tree를 구하는 알고리즘 중 하나인 Kruskal Algorithm이 Union-Find를 이용해 구현한다.

어떠한 두 정점을 연결하고자 할 때, 연결되어 있음이 확인될 경우 cycle이 형성된다는 아이디어를 이용한다.

보통 Union-Find는 parent 배열, height 배열을 이용해서 구현한다.

height 배열은 시간복잡도 최적화를 하기 위해 설정한다.

만약 height가 없다면 $union, find$ 모두 $O(n)$인데, height 배열을 이용해 탐색 횟수를 줄여주면 거의 $O(1)$으로 줄어든다.

 

초기화는 다음과 같이 한다.

int parent[n];
int height[n];
for (int i = 0 ; i < n; i++) {
    parent[i] = i;
    height[i] = 0;
}

$union(a, b)$를 할 때,

while (parent[a] != a) a = parent[a];
while (parent[b] != b) b = parent[b];

if (a != b) {
    if (height[a] == height[b]) {
        parent[b] = a; height[a]++;
    }
    else if (height[a] > height[b]) parent[b] = a;
    else parent[a] = b;
}

$a$와 $b$가 다를 때 연결을 한다. 만약 $a = b$일 때 연결을 하면 cycle이 형성된다.


$find(a, b)$를 할 때,

while (parent[a] != a) a = parent[a];
while (parent[b] != b) b = parent[b];

if (a == b) printf("YES\n");
else printf("NO\n"); 

$a$와 $b$가 같다면 연결이 되어 있다.


이 Union-Find 자료구조를 이용해 MST를 구현하면 다음과 같다. (크루스칼 알고리즘)

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

int main () {
    int n, m;
    scanf("%d %d", &n, &m);
    vector<pair<double, pair<int, int>>> edge;

    for (int i = 0; i < m; i++) {
        int a, b; double c;
        scanf("%d %d %lf", &a, &b, &c);
        a--; b--;
        edge.push_back(make_pair(c, make_pair(a, b)));
    }
    sort(edge.begin(), edge.end());

    int parent[n];
    int height[n];
    for (int i = 0 ; i < n; i++) {
        parent[i] = i;
        height[i] = 0;
    }
    double cost = 0;

    for (int i = 0; i < edge.size(); i++) {
        int x = edge[i].second.first;
        int y = edge[i].second.second;
        double w = edge[i].first;
        while (parent[x] != x) x = parent[x];
        while (parent[y] != y) y = parent[y];

        if (x != y) {
            cost += w;
            if (height[x] == height[y]) {
                parent[y] = x;
                height[x]++;
            }
            else if (height[x] < height[y]) parent[x] = y;
            else parent[y] = x;
        }
    }
    printf("%.0lf", cost);
}
728x90

'C++ 코딩테스트 > 코딩테스트 개념잡기' 카테고리의 다른 글

백트래킹의 기본  (0) 2022.06.19

+ Recent posts