콘텐츠로 이동

최소 신장 트리 - 서로소 집합 유니온을 이용한 Kruskal 알고리즘

MST 문제와 Kruskal 알고리즘에 대한 설명은 먼저 Kruskal 알고리즘 메인 문서를 참고하십시오.

이 문서에서는 Kruskal 알고리즘을 구현하기 위해 "서로소 집합 유니온" 자료 구조를 살펴봅니다. 이 자료 구조를 사용하면 알고리즘이 $O(M \log N)$의 시간 복잡도를 달성할 수 있습니다.

설명

단순한 버전의 Kruskal 알고리즘과 마찬가지로, 그래프의 모든 간선을 가중치의 비내림차순으로 정렬합니다. 그런 다음 make_set 함수를 호출하여 각 정점을 자기 자신만의 트리(즉, 자신의 집합)에 넣습니다. 이 과정에는 총 $O(N)$이 걸립니다. (정렬된 순서대로) 모든 간선을 순회하면서, 각 간선에 대해 두 끝점이 서로 다른 트리에 속하는지 판별합니다(각각 $O(1)$인 find_set 호출 두 번으로). 마지막으로 두 트리(집합)를 합쳐야 하며, 이를 위해 DSU의 union_sets 함수를 호출합니다. 이 역시 $O(1)$입니다. 따라서 전체 시간 복잡도는 $O(M \log N + N + M)$ = $O(M \log N)$입니다.

구현

다음은 랭크에 의한 합치기(Union by Rank)를 사용한 Kruskal 알고리즘의 구현입니다.

vector<int> parent, rank;

void make_set(int v) {
    parent[v] = v;
    rank[v] = 0;
}

int find_set(int v) {
    if (v == parent[v])
        return v;
    return parent[v] = find_set(parent[v]);
}

void union_sets(int a, int b) {
    a = find_set(a);
    b = find_set(b);
    if (a != b) {
        if (rank[a] < rank[b])
            swap(a, b);
        parent[b] = a;
        if (rank[a] == rank[b])
            rank[a]++;
    }
}

struct Edge {
    int u, v, weight;
    bool operator<(Edge const& other) {
        return weight < other.weight;
    }
};

int n;
vector<Edge> edges;

int cost = 0;
vector<Edge> result;
parent.resize(n);
rank.resize(n);
for (int i = 0; i < n; i++)
    make_set(i);

sort(edges.begin(), edges.end());

for (Edge e : edges) {
    if (find_set(e.u) != find_set(e.v)) {
        cost += e.weight;
        result.push_back(e);
        union_sets(e.u, e.v);
    }
}

참고: MST는 정확히 $N-1$개의 간선을 포함하므로, 그만큼의 간선을 찾으면 for 루프를 멈출 수 있습니다.

연습 문제

이 주제에 관한 연습 문제 목록은 Kruskal 알고리즘 메인 문서를 참고하십시오.