Coding Test/알고리즘 이론
최소 신장 트리(Prim's, MST: Minimum spanning Tree)
hyunkookim
2025. 4. 11. 03:44
✅ 1. 최소 신장 트리(Minimum Spanning Tree)란?
- 정의: 주어진 무방향 연결 그래프에서 모든 정점들을 연결하면서 가중치의 합이 최소가 되는 트리 구조의 부분 그래프.
- 조건
- 모든 정점이 연결되어 있어야 함.
- 사이클(순환)은 없어야 함.
- 여러 개의 MST가 존재할 수도 있음.
- 📌 기본 개념
- Undirected Graph (무방향 그래프)
- 모든 노드가 연결되도록 (Connected)
- 전체 간선의 가중치 합이 최소가 되도록 (Minimum Total Cost)
- 노드가 n개라면 MST는 항상 n - 1개의 간선만 포함
→ 그래서 사이클이 생기지 않음 - 어느 노드에서 시작해도 결과는 동일한 MST 완성 가능
✅ 2. Prim's Algorithm이란? (프림 알고리즘)
- 핵심 아이디어: 하나의 정점에서 시작해서, 가장 작은 가중치의 간선을 하나씩 선택하며 MST를 만들어가는 방식.
💡 동작 방식:
- 임의의 정점에서 시작 (예: 0번 정점)
- 현재 선택된 정점들과 연결된 간선 중 가장 작은 가중치의 간선을 선택
- 그 간선으로 연결된 새로운 정점을 트리에 추가
- 위 과정을 모든 정점이 포함될 때까지 반복
🔧 사용 자료구조
- visited: 방문한 노드들을 저장 (보통 set/hashset)
- minHeap (우선순위 큐):
- 형식: [weight, n1, n2]
→ weight: 간선 가중치
→ n1: 현재 방문 중인 노드
→ n2: 연결할 다음 노드 - 항상 가중치가 가장 작은 간선부터 탐색
- 형식: [weight, n1, n2]
🔄 전체 알고리즘 흐름
- 아무 노드에서 시작 (예: 노드 0)
→ visited에 추가하고, 연결된 모든 간선을 minHeap에 넣음 - minHeap에서 가장 작은 간선을 꺼냄
- to_node가 아직 방문하지 않았다면:
- 간선 선택 (MST에 포함)
- to_node를 방문 처리 (visited에 추가)
- to_node에서 뻗는 간선들을 minHeap에 추가
- 이 과정을 n-1개의 간선이 선택될 때까지 반복
import heapq # 최소 힙(min heap)을 구현하기 위한 heapq 모듈 임포트
# 주어진 무방향 연결 그래프의 간선 리스트와 노드 개수 n을 받아,
# 최소 신장 트리를 구성하는 간선들의 리스트를 반환하는 함수
def minimumSpanningTree(edges, n):
adj = {} # 인접 리스트로 그래프 표현
for i in range(1, n + 1):
adj[i] = [] # 각 노드를 키로 하여 빈 리스트 초기화
# 간선 정보를 인접 리스트 형태로 구성 (무방향이므로 양쪽에 모두 추가)
for n1, n2, weight in edges:
adj[n1].append([n2, weight])
adj[n2].append([n1, weight])
# 최소 힙 초기화
# 시작 노드는 1번 노드로 설정하고, 해당 노드에 연결된 모든 간선을 힙에 추가
minHeap = []
for neighbor, weight in adj[1]:
heapq.heappush(minHeap, [weight, 1, neighbor]) # 형식: [가중치, 현재 노드, 다음 노드]
mst = [] # 최소 신장 트리에 포함될 간선들을 저장할 리스트
visit = set() # 방문한 노드들을 저장할 집합
visit.add(1) # 시작 노드 1번을 방문 처리
# 모든 노드가 방문될 때까지 반복
while len(visit) < n:
weight, n1, n2 = heapq.heappop(minHeap) # 가장 가중치가 낮은 간선 꺼냄
if n2 in visit:
continue # 이미 방문한 노드면 무시하고 다음 반복
mst.append([n1, n2]) # 최소 신장 트리에 현재 간선 추가
visit.add(n2) # 새로 연결된 노드를 방문 처리
# 새로 방문한 노드에서 연결되는 간선들을 힙에 추가
for neighbor, weight in adj[n2]:
if neighbor not in visit:
heapq.heappush(minHeap, [weight, n2, neighbor]) # 형식: [가중치, 현재 노드, 다음 노드]
return mst # 최소 신장 트리에 포함된 간선 리스트 반환
✅ 추가 설명
- MST 구성만 하고 전체 비용은 리턴하지 않는 구조입니다.
→ 필요하다면 mst_cost += weight 로 가중치를 누적해 따로 반환 가능해요. - 시작 노드가 항상 1번으로 고정되어 있지만, 그래프가 연결되어 있으면 어디서 시작하든 결과는 같아요.
✅ 3. 시간복잡도
- 인접 행렬 + 우선순위 큐 없음: O(V^2)
- 인접 리스트 + 우선순위 큐 사용 (Heap 사용): O(E log V)
✅ 예시
[0] -------- (1) -------- [1]
| |
(4) (3)
| |
[2] -------- (2) -------- [3]
시작: 노드 0
- Heap: [(1, 0, 1), (4, 0, 2)] → 1 선택 → 방문: 0, 1
- Heap: [(3, 1, 3), (4, 0, 2)] → 2 선택 → 방문: 0, 1, 3
- Heap: [(2, 3, 2), (4, 0, 2)] → 3 선택 → 방문: 0, 1, 2, 3
- 종료 (MST 완성)
[정점]: A, B, C, D
[간선] (가중치):
A - B (1)
A - C (3)
B - C (1)
B - D (4)
C - D (2)
▶ Prim's 시작: A
1. A-B (1) 선택 → 포함: A, B
2. B-C (1) 선택 → 포함: A, B, C
3. C-D (2) 선택 → 포함: A, B, C, D
==> 총 가중치: 1 + 1 + 2 = 4
✅ 핵심 요약 포인트
- MST는 노드가 n개일 때 n-1개의 간선만 필요
- 따라서 불필요한 간선을 없애는 문제로 바꿔서 생각할 수 있음
- Prim은 방문 노드를 확장하면서 가장 가벼운 간선을 선택해 나가는 방식
- 시작 노드는 아무거나 가능
- Cycle은 절대 생기지 않음 (visited로 방지)