[Data Structure] 최소 신장 트리 알고리즘(MST / Minimum Spanning Tree)
🙋♀️MST(Minimum Spanning Tree)
신장 트리: 사이클이 형성되지 않는 그래프
최소 신장 트리: 신장 트리의 모든 간선의 가중치 합이 최소인 그래프
🙂 지금부터 대표적인 최소 신장 트리 구현 알고리즘인 크루스칼과 프림 알고리즘을 빠르게 살펴보고자 한다!
🙋♀️ 크루스칼 알고리즘
가중치를 기준으로 간선을 정렬한 후 MST가 될 때까지 간선을 하나씩 선택하는 방식
- 그리디 알고리즘의 일종이다.
- 순간마다 가중치 합이 최소인 것을 탐색하기 때문이다.
- 순서
- 가중치를 기준으로 간선들을 오름차순 정렬한다.
- N-1개 간선이 선택될 때까지 다음의 과정을 반복한다. (MST가 될 때까지)
- 가중치가 가장 낮은 간선을 탐색한다.
- union-find 알고리즘을 통해 사이클이 형성되는지 판단한다.
- 사이클이 형성되지 않을 경우 간선을 선택한다.
🧐 Union-Find 알고리즘
두 개의 노드의 루트를 확인해 서로 같은 그래프에 속하는지를 판별하는 알고리즘
union
- 두 개의 집합을 하나의 집합으로 합치는 연산
- 즉, 두 집합의 루트를 동일하게 만드는 연산이다!
find
- 원소의 루트를 반환한다.
- 두 개의 집합의 노드가 서로 같은지를 판단할 수 있다!
🙄 아마 코드를 통해 과정을 살펴본다면 이해가 잘 될 것이다!
🧐 크루스칼 알고리즘 with 파이썬
graph = [(2, 4, 13), (1, 3, 12)...] # (정점1, 정점2, 가중치)
graph.sort(key = lambda x: x[2]) # 가중치로 오름차순 정렬
mst = []
n = 7 # 정점 개수
p = [0] # 각 노드의 루트를 표시
for i in range(1,n+1):
P.append(i) # 처음, 정점들의 루트는 자기 자신
def find(u): # find 연산
if u != p[u]:
p[u] = find(p[u])
return p[u]
def union(u, v): # union 연산
r1 = find(u)
r2 = find(v)
p[r2] = r1 # 루트를 동일하게 만듬(임의로 r1을 r2의 부모로 설정)
edges = 0 # 간선의 수
cost = 0 # 가중치의 합
while 1:
if edges == n-1:
break
u, v, c = graph.pop(0)
if find(u) != find(v):
union(u, v)
mst.append((u, v))
cost += c
edges += 1
print(mst, cost) # 최소 신장 트리, 가중치
🙋♀️ 프림 알고리즘
하나의 정점을 시작으로 MST가 될 때까지 트리를 확장해 나가는 방식
- 크루스칼 알고리즘과 마찬가지로 그리디 알고리즘의 일종이다.
- 순서
- 하나의 시작 정점을 선택하여 시작 정점만 포함된 MST 집합을 만든다.
- N개의 정점이 모두 선택될 때까지 다음의 과정을 반복한다.
- MST 집합에 포함된 정점에 인접하고, 방문되지 않은 정점 중 최소 비용 간선으로 연결된 정점을 선택한다.
- 해당 정점을 MST 집합에 포함시킨다.
🧐 프림 알고리즘 with python
from collections import defaultdict
from heapq import *
def prim(start, edges):
mst = []
adjacent_edges = defaultdict(list)
for n1, n2, weight in edges:
adjacent_edges[n1].append((weight, n1, n2))
adjacent_edges[n2].append((weight, n2, n1))
connected_nodes = set(start)
candidate = adjacent_edges[start]
heapify(candidate)
while candidate:
weight, n1, n2 = heappop(candidate)
if n2 not in connected_nodes:
connected_nodes.add(n2)
mst.append((weight, n1, n2))
for edge in adjacent_edges[n2]:
if edge[2] not in connected_nodes:
heappush(candidate, edge)
🙋♀️ 크루스칼 vs 프림
시간 복잡도
- 크루스칼: O(ElogE)
- 프림: O(ElogE)
- 개선된 프림 알고리즘으로 구현시 O(ElogV)의 시간 복잡도를 갖게 된다.
- 사용
- 정점 수 > 간선 수 (희소 그래프) => 크루스칼 알고리즘 적합
- 정점 수 < 간선 수 (밀집 그래프) => 프림 알고리즘 적합
🙇♀️ 부족한 부분이 있다면 말씀해주세요! 감사합니다!
📃참고
- 크루스칼 알고리즘
- 프림 알고리즘
댓글남기기