에듀테크 프로젝트 대시보드
최소 신장 트리 (MST)
여러 도시를 도로로 모두 연결하되, 깔린 도로의 길이 합을 최소로 하려면? 이게 MST 문제다.
정점 V개를 사이클 없이 이으려면 정확히 V-1개의 간선이 필요하고,
그중 가중치 합이 가장 작은 연결 방법이 최소 신장 트리다.
두 대표 알고리즘 모두 탐욕적(greedy)으로 동작한다 —
Kruskal은 가장 싼 간선부터 훑으며 사이클만 아니면 채택하고,
Prim은 한 정점에서 나무를 키우며 매번 나무에 닿는 가장 싼 간선을 붙인다.
신기하게도 매 순간 "지금 제일 싼 것"만 골라도 전체 최적이 보장된다(절단 성질, cut property).
def kruskal(n, edges): # edges = [(w, u, v), ...] parent = list(range(n)) def find(x): while parent[x] != x: parent[x] = parent[parent[x]] # 경로 압축 x = parent[x] return x mst, total = [], 0 for w, u, v in sorted(edges): # 가중치 오름차순 ru, rv = find(u), find(v) if ru != rv: # 두 정점이 다른 그룹이면 = 사이클 아님 parent[ru] = rv # 합치고 채택 mst.append((u, v, w)); total += w return mst, total # 간선 V-1개 모이면 완성
find 두 번으로 O(α(n))에 판정.O(E log E)로 지배적 → 전체 O(E log E). 간선이 적은(희소) 그래프에 유리.import heapq def prim(n, adj, start=0): # adj[u] = [(w, v), ...] in_tree = [False] * n pq = [(0, start, -1)] # (간선 비용, 도착 정점, 출발 정점) mst, total = [], 0 while pq and len(mst) < n - 1: w, v, u = heapq.heappop(pq) if in_tree[v]: continue # 이미 나무 안 → 이 간선은 사이클 in_tree[v] = True if u != -1: mst.append((u, v, w)); total += w for nw, nx in adj[v]: # 새로 닿게 된 경계 간선들 추가 if not in_tree[nx]: heapq.heappush(pq, (nw, nx, v)) return mst, total
O(E log V), 피보나치 힙이면 O(E + V log V). 간선이 많은(조밀) 그래프에 유리.| 구분 | Kruskal | Prim |
|---|---|---|
| 고르는 방식 | 전체에서 싼 간선 순서대로 | 나무에 닿는 싼 간선부터 |
| 중간 상태 | 여러 조각(숲)이 합쳐짐 | 항상 하나의 연결된 나무 |
| 핵심 자료구조 | 정렬 + 유니온-파인드 | 우선순위 큐(최소 힙) |
| 시간복잡도 | O(E log E) | O(E log V) |
| 유리한 그래프 | 희소(간선 적음) | 조밀(간선 많음) |