본문으로 건너뛰기
김신건의 로그

Spanning Tree, MST: 최소 신장 트리

· 수정 · 📖 약 4분 · 1,293자/단어 #algorithm #graph #mst #kruskal #prim
Spanning Tree, Minimum Spanning Tree, MST, 최소 신장 트리, 신장 트리

정의

Spanning Tree 는 그래프의 모든 정점을 포함하는 부분 트리입니다. 정점 N 개이면 간선 N-1 개.

MST (Minimum Spanning Tree) 는 간선 가중치 합이 최소인 spanning tree.

  • MST 는 유일하지 않을 수 있음 (같은 가중치 간선이 존재할 때)
  • 가중치 합이 같은 MST 가 여러 개이면 모두 최적

문제 상황

N 개 도시를 최소 비용으로 연결하는 도로 네트워크, 전기 배선, 클러스터링 등.

  • 간선 수 E 가 많을 때: Kruskal (E log E 정렬 지배)
  • 정점 수 V 가 많고 간선이 dense 할 때: Prim (priority queue, E log V)
  • 병렬 처리가 필요할 때: Boruvka (O(E log V))
알고리즘시간적합
KruskalO(E log E)sparse graph
PrimO(E log V)dense graph
BoruvkaO(E log V)병렬/외부 메모리

시각화

6개 정점의 weighted 그래프에서 MST 선택 과정.

flowchart LR
    A((A))
    B((B))
    C((C))
    D((D))
    E((E))
    F((F))

    A ---|"1"| B
    A ---|"4"| C
    B ---|"2"| C
    B ---|"5"| D
    C ---|"3"| E
    D ---|"6"| F
    E ---|"2"| F

    style A fill:#d4edda
    style B fill:#d4edda
    style C fill:#d4edda
    style E fill:#d4edda
    style F fill:#d4edda
    style D fill:#d4edda

Kruskal 로 간선 (1), (2), (2), (3) 선택 → 총 비용 8. 사이클 없이 N-1 = 5 간선.

MST 간선 시각화 (굵게 표시 대신 순서 설명):

  1. A-B 가중치 1 (선택)
  2. B-C 가중치 2 (선택)
  3. E-F 가중치 2 (선택)
  4. C-E 가중치 3 (선택)
  5. B-D 가중치 5 (선택, 마지막 연결)

핵심 아이디어

Cut Property (MST 보장 원리)

어떤 컷 (S, V-S) 에 대해 크로스 간선 중 최소 가중치 간선은 반드시 MST 에 포함됩니다.

Kruskal 과 Prim 은 모두 이 cut property 를 그리디하게 활용합니다.

Cycle Property

사이클의 최대 가중치 간선은 MST 에 포함되지 않습니다.

알고리즘

Kruskal

  1. 모든 간선을 가중치 오름차순 정렬
  2. 간선 (u, v, w) 를 순서대로 확인
  3. u 와 v 가 다른 컴포넌트이면 (Union-Find) 간선 추가
  4. N-1 개 간선이 선택되면 종료
sort edges by weight ascending
dsu = Union-Find(n)
mst_weight = 0
for (w, u, v) in edges:
    if dsu.find(u) != dsu.find(v):
        dsu.union(u, v)
        mst_weight += w

Prim

  1. 임의 시작 정점 선택, priority queue 에 삽입
  2. 최소 가중치 간선으로 확장 가능한 정점 반복 추가
  3. 이미 방문한 정점은 스킵
vis = [false] * n
pq = min-heap [(0, start)]
mst_weight = 0
while pq not empty:
    (w, u) = pq.pop()
    if vis[u]: continue
    vis[u] = true
    mst_weight += w
    for (v, ew) in adj[u]:
        if not vis[v]: pq.push((ew, v))

Boruvka

  1. 각 컴포넌트에서 최소 outgoing edge 동시 선택
  2. 선택한 간선으로 컴포넌트 합병
  3. 컴포넌트가 1개가 될 때까지 반복 (최대 log V 라운드)

구현

MST 두 가지 방법으로
C++ (Kruskal + Prim)
#include <bits/stdc++.h>
using namespace std;

// ---- Union-Find ----
struct DSU {
  vector<int> p, rank_;
  DSU(int n) : p(n), rank_(n, 0) { iota(p.begin(), p.end(), 0); }
  int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); }
  bool merge(int x, int y) {
      x = find(x); y = find(y);
      if (x == y) return false;
      if (rank_[x] < rank_[y]) swap(x, y);
      p[y] = x;
      if (rank_[x] == rank_[y]) rank_[x]++;
      return true;
  }
};

// ---- Kruskal ----
long long kruskal(int n, vector<tuple<int,int,int>>& edges) {
  sort(edges.begin(), edges.end());
  DSU dsu(n);
  long long total = 0;
  for (auto& [w, u, v] : edges)
      if (dsu.merge(u, v)) total += w;
  return total;
}

// ---- Prim ----
long long prim(int n, vector<vector<pair<int,int>>>& adj) {
  vector<bool> vis(n, false);
  priority_queue<pair<int,int>,
                 vector<pair<int,int>>,
                 greater<>> pq;
  pq.push({0, 0});
  long long total = 0;
  while (!pq.empty()) {
      auto [w, u] = pq.top(); pq.pop();
      if (vis[u]) continue;
      vis[u] = true;
      total += w;
      for (auto [v, ew] : adj[u])
          if (!vis[v]) pq.push({ew, v});
  }
  return total;
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int n, m;
  cin >> n >> m;
  vector<tuple<int,int,int>> edges;
  vector<vector<pair<int,int>>> adj(n);
  for (int i = 0; i < m; i++) {
      int u, v, w; cin >> u >> v >> w; u--; v--;
      edges.push_back({w, u, v});
      adj[u].push_back({v, w});
      adj[v].push_back({u, w});
  }
  cout << "Kruskal: " << kruskal(n, edges) << "\n";
  cout << "Prim:    " << prim(n, adj) << "\n";
  return 0;
}
결과
// 예: 4 정점, 5 간선
// 0-1:1, 0-2:4, 1-2:2, 1-3:5, 2-3:3
Kruskal: 6
Prim:    6

복잡도

알고리즘시간공간비고
KruskalO(E log E)O(V)Union-Find path compression
Prim + 이진 힙O(E log V)O(V + E)priority_queue
Prim + Fibonacci 힙O(E + V log V)O(V + E)이론적 최적, 구현 복잡
BoruvkaO(E log V)O(V + E)병렬 친화적

실전: E ~ V² (dense) 이면 Prim, E ~ V (sparse) 이면 Kruskal 이 더 나은 경향.

함정

WARNING

비연결 그래프에서 MST 미정의: 그래프가 연결되지 않으면 spanning tree 자체가 없음. Kruskal 에서 선택한 간선 수가 N-1 개인지 확인해야 함.

WARNING

Prim 에서 vis 배열 없이 사용: priority queue 에서 이미 방문한 정점이 다시 나올 수 있음. vis[u] 체크 없으면 간선 중복 처리 → 오답.

CAUTION

방향 그래프 (Directed) 에서 MST: Kruskal/Prim 은 무방향 그래프 전용. 방향 그래프의 최소 신장 구조는 DMST (Directed MST = 최소 비용 수형도) 로 Edmonds’ algorithm (O(VE)) 를 사용해야 함.

  • 간선 가중치가 같을 때 MST 가 여러 개이면, 특정 MST 를 구하라는 조건이 붙는 경우가 있음.
  • Second MST (두 번째 최소 신장 트리): MST 에서 간선 하나 교체.

응용

  • 네트워크 설계: 최소 비용으로 전체 연결
  • 클러스터링: MST 에서 가장 긴 간선을 자르면 k 개 클러스터
  • Steiner Tree: 일부 정점만 연결하는 최소 트리 (NP-hard 일반, 소수 정점에는 DP)
  • Offline Dynamic Connectivity: Boruvka 기반 병렬 Union-Find

BOJ

문제설명
BOJ 1197 최소 스패닝 트리MST 기본
BOJ 1922 네트워크 연결Kruskal/Prim 선택
BOJ 6497 전력난MST 비용 계산
BOJ 17472 다리 만들기 2BFS + Kruskal
BOJ 4386 별자리 만들기완전 그래프 MST

관련 위키

이 글의 용어 (5개)
분리 집합 (Disjoint Set, Union-Find)algorithm
정의 분리 집합 (Disjoint Set, Union-Find) 은 서로 겹치지 않는 집합들을 관리하며, 다음 두 연산을 거의 상수 시간 (amortized O(α(N)), α는…
최단 경로 (Shortest Path)algorithm
정의 최단 경로 (Shortest Path) 는 그래프 G = (V, E) 에서 시작 정점 s 에서 목표 정점 t 까지 가는 경로 중 가중치 합이 최소인 경로 를 찾는 문제. 단…
Directed MSTalgorithm
정의 Directed MST (Arborescence) 는 유향 그래프와 루트 이 주어졌을 때, 에서 모든 정점으로 도달 가능 한 간선들의 최소 가중치 부분집합을 구하는 문제. …
Graph DP: 트리/DAG 위 DPalgorithm
정의 Graph DP 는 그래프 구조를 이용한 DP. 사이클 없는 그래프 (트리, DAG) 에서 특히 자연스럽다. - Tree DP: 루트 트리에서 자식 서브트리 결과를 조합 -…
Priority Queue / Heap: 우선순위 큐algorithm
정의 Priority Queue 는 우선순위가 가장 높은 원소를 O(log N) 에 pop 할 수 있는 자료구조. Binary Heap 이 표준 구현. - Max-heap: 부모…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기