📌최소 신장 트리
💡 그래프에서의 최소 비용 문제
- 모든 정점을 연결하는 간선들의 가중치의 합이 최소가 되는 트리
(최소 신장 트리)
- 두 정점 사이의 최소 비용의 경로 찾기
(최단 경로)
신장 트리
- n개의 정점으로 이루어진 무향 그래프에서
n개의 정점
과 n-1개의 간선
으로 이루어진 트리
- 어떤 정점도 외톨이가 되지 않도록 연결된 트리. BUT 사이클이 생기면 안된다!
최소 신장 트리 (Minimum Spanning Tree)
- 무향 가중치 그래프에서 신장 트리를 구성하는 간선들의 가중치의 합이 최소인 신장 트리
📌KRUSKAL 알고리즘
- 간선을 하나씩 선택해서 MST를 찾는 알고리즘
- 간선을 이용하기 때문에
리스트
를 활용한다.
💡 알고리즘
- 최초, 모든 간선을 가중치에 따라 오름차순으로 정렬
- 가중치가 가장 낮은 간선부터 선택하면서 트리를 증가시킴.
❗사이클이 존재하면 다음으로 가중치가 낮은 간선 선택
- N-1개의 간선이 선택 될 때까지 2를 반복.
💡 알고리즘 적용 예
💡 코드
public class Kruskal {
static class Edge implements Comparable<Edge> {
int start, end, weight;
public Edge(int start, int end, int weight) {
super();
this.start = start;
this.end = end;
this.weight = weight;
}
@Override
public int compareTo(Edge o) {
return Integer.compare(this.weight, o.weight);
}
}
static int[] parents;
private static void make() {
parents = new int[V];
for (int i = 0; i < V; i++) {
parents[i] = i;
}
}
private static int find(int a) {
if (a == parents[a]) return a;
return parents[a] = find(parents[a]);
}
private static boolean union(int a, int b) {
int aRoot = find(a);
int bRoot = find(b);
if (aRoot == bRoot) return false;
parents[bRoot] = aRoot;
return true;
}
static int V, E;
static Edge[] edgeList;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
V = Integer.parseInt(st.nextToken());
E = Integer.parseInt(st.nextToken());
edgeList = new Edge[E];
for (int i = 0; i < E; i++) {
st = new StringTokenizer(br.readLine());
int start = Integer.parseInt(st.nextToken());
int end = Integer.parseInt(st.nextToken());
int weight = Integer.parseInt(st.nextToken());
edgeList[i] = new Edge(start, end, weight);
}
Arrays.sort(edgeList);
make();
int cnt = 0, result = 0;
for (Edge edge : edgeList) {
if (union(edge.start, edge.end)) {
result += edge.weight;
if (++cnt == V - 1) break;
}
}
System.out.println(result);
}
}
📌PRIM 알고리즘
- 하나의 정점에서 연결된 간선들 중에 하나씩 선택하면서 MST를 만들어가는 방식
- 정점 중심이기 때문에
인접행렬, 인접 리스트
를 활용한다.
💡 알고리즘
- 임의 정점을 하나 선택해서 시작
- 선택한 정점과 인접하는 정점들 중의 최소 비용의 간선이 존재하는 정점 선택
- 모든 정점이 선택될 때까지 1,2 과정을 반복
- 간선이 많으면 kruskal보다 prim이 더 효율적이다.
- kruskal은 간선을 정렬해야 하기 때문에 prim보다 비효율적일 수 있다.
💡 알고리즘 적용 예
💡 코드
public class PrimTest {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
int[][] adjMatrix = new int[N][N];
boolean[] v = new boolean[N];
int[] minEdge = new int[N];
for (int i = 0; i < N; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
for (int j = 0; j < N; j++) {
adjMatrix[i][j] = Integer.parseInt(st.nextToken());
}
minEdge[i] = Integer.MAX_VALUE;
}
int result = 0;
minEdge[0] = 0;
for (int i = 0; i < N; i++) {
int min = Integer.MAX_VALUE;
int minVertex = -1;
for (int j = 0; j < N; j++) {
if (!v[j] && min > minEdge[j]) {
min = minEdge[j];
minVertex = j;
}
}
v[minVertex] = true;
result += min;
for (int j = 0; j < N; j++) {
if (!v[j] && adjMatrix[minVertex][j] != 0 && minEdge[j] > adjMatrix[minVertex][j])
minEdge[j] = adjMatrix[minVertex][j];
}
}
System.out.println(result);
}
}