
선수과목
V개의 정점(Vertex), E개의 간선(Edge) 가 있는 그래프를 가정
최소 신장 트리 (MST, Minimum Spanning Tree)
신장트리 : 그래프에서 모든 정점을 연결하고 있는 트리
최소신장트리 : 간선의 가중치(거리)가 최소인 신장트리
간선의 개수가 V-1 인 특징이 있다.
간선들 중에서 빨간색으로 선택된 간선들이 최소 가중치를 가지면서 모든 노드들을 연결한다.
MST 는 그리디 기법을 이용하여 최적의 해를 구할 수 있으며,
대표적으로 크루스칼 알고리즘과 프림 알고리즘이 있다.
오늘은 크루스칼 알고리즘에 대해 알아보자.
MST 찾기 알고리즘 - 간선 리스트(Edge List) 중심
매 순간 최선의 선택을 하기에 그리디 알고리즘으로 분류된다
경로 최적화 이용 X :
경로 최적화 이용 O :
알고리즘 과정
- 가장 낮은 가중치의 간선 선택
A - B (가중치 2)
![]()
- 그다음 낮은 가중치의 간선 선택
가중치가 3인 간선이 2개 있으나, 아무거나 선택
E - D (가중치 3)
![]()
- 그다음 낮은 가중치의 간선 선택
방금 선택하지 않은 가중치 3인 간선 선택
B - C (가중치 3)
- 그다음 낮은 가중치의 간선 선택
⚠️ 이 사진에서만 A - D (가중치 4) 간선을 넣어봤습니다. ⚠️
이미 그림을 다 그렸었지만 싸이클이 발생하는 예시를 보여주고 싶었습니다A - D (가중치 4) 가 그 다음으로 가중치가 낮은 간선이지만, 싸이클이 발생하기 때문에 선택하지 않고 넘어간다.
![]()
- 그다음 낮은 가중치의 간선 선택
A - B (가중치 5)
![]()
총 간선의 개수가 V-1 인 4개 이기 때문에 MST 완성 !
가중치는 총 2+3+3+5 = 13 이다
Pseudo Code, 수도코드
// G.V : 그래프의 정점 집합, G.E : 그래프의 간선 집합
// n : 정점 수, cnt : 선택한 간선 수, weight : 선택한 간선들의 가중치 합
MST-KRUSKAL(G, w)
cnt = 0, weight = 0;
FOR vertex v in G.V
Make-Set(v)
End For
Sort(G.E) // G.E 에 포함된 간선들을 가중치 w를 이용한 오름차순 정렬
FOR 간선 (u, v) ∈ G.E 선택 // 신장 트리의 구성으로 선택한 간선의 개수가 n-1개가 될 때까지 반복
IF Find-Set(u) != Find-Set(v) THEN
Union(u,v)
cnt++ // 총 간선의 수 증가
weight = weight + w // 총 가중치 증가
IF cnt == n-1 THEN // MST 완성
break
END IF
END IF
END FOR
END MST-KRUSKAL()
Java Code
public class MST_Test {
static class Edge implements Comparable<Edge> {
int from, to, weight;
public Edge(int from, int to, int weight) {
super();
this.from = from;
this.to = to;
this.weight = weight;
}
@Override
public int compareTo(Edge o) {
return Integer.compare(this.weight, o.weight);
}
}
static int V; // 정점 개수
static Edge[] edgeList;
static int[] parents;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
V = Integer.parseInt(st.nextToken()); // 정점의 개수 입력
int E = Integer.parseInt(st.nextToken()); // 간선의 개수 입력
edgeList = new Edge[E];
// from to weigth 로 간선 정보의 입력이 들어온다고 가정
for (int i=0; i<E; i++) {
st = new StringTokenizer(br.readLine(), " ");
int from = Integer.parseInt(st.nextToken());
int to = Integer.parseInt(st.nextToken());
int weight = Integer.parseInt(st.nextToken());
edgeList[i] = new Edge(from, to, weight);
} // 간선 리스트 생성
// 전처리 : 간선 리스트 오름차순 정렬
Arrays.sort(edgeList);
// 1. make - set
make();
// 2. 정렬된 간선하나씩 꺼내어 신장트리 생성
int weight = 0;
int cnt = 0;
for (Edge edge : edgeList) {
if (!union(edge.from, edge.to)) continue;
weight += edge.weight;
if (++cnt == V-1) break; // 최소신장트리(MST) 완성
}
System.out.println(weight);
}
public static void make() {
parents = new int[V+1]; // 자신의 부모나 루트(경로 압축) 저장
for (int i=1; i<=V; i++) {
parents[i] = i; // 모든 정점을 자신을 루트로
}
}
public static int find(int a) { // a가 속한 트리의 루트 찾기
if (a == parents[a]) return a;
return parents[a] = find(parents[a]); // 경로 압축
}
public static boolean union(int a, int b) {
int aRoot = find(a);
int bRoot = find(b);
if (aRoot == bRoot) return false; // a,b 가 같은 트리에 속해있다 => union 불필요
parents[bRoot] = aRoot;
return true;
}
}

MST의 가중치를 출력하는 문제.
위 java 예시 코드를 그대로 넣으면 정답이다.
더 많은 문제들 ↓