최소 신장 트리
최소 신장트리
사용한 간선들의 가중치 합이 최소
간선 가중치를 고려하여 최소 비용의 신장 트리 선택
그래프의 모든 노드 포함
모든 노드는 서로 연결
사이클 X
Kruskal 알고리즘, Prim 알고리즘으로 구현
Kruskal 알고리즘
가장 적은 비용으로 모든 노드 연결
간선 선택 기반
서로소 집합(Disjoint Set) / Union-Find 알고리즘 적용
그래프를 간선 리스트로 표현
순서
모든 가중치를 오름차순으로 정렬
가중치가 가장 작은 간선 선택
해당 간선이 연결하려는 노드가 서로 연결되지 않은 상태라면 2개의 노드 연결
1~3 과정을 반복
Disjoint Set 서로소 집합
공통 원소가 없는 두 집합
union, find 두 가지 연산
union
- 두 개의 원소가 각각 포함되어 있는 집합을 하나로 합침
find
- 특정한 원소가 속한 집합이 어떤 집합인지 찾음
하나의 트리를 하나의 집합으로 볼 때, find는 트리의 루트 노드를 찾고, 그 루트 노드를 통해 특정 집합 표현
union은 두 원소에 대해 find 연산을 수행하여 각각의 루트 노드를 찾고, 한 쪽의 루트 노드를 다른 쪽에 연결함으로써 하나의 트리로 만드는 합집합 연산 수행
static int find_parent(int x) {
if (parent[x] != x) {
return find_parent(parent[x]);
}
return x;
}
// 경로 압축 기법 적용
static int find_parent_compress(int x){
if (parent[x] != x) {
parent[x] = find_parent(parent[x]);
}
return parent[x];
}
static void union_parent(int a, int b){
a = find_parent(a);
b = find_parent(b);
if (a < b) {
parent[b] = a;
} else {
parent[a] = b;
}
}
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
int V = sc.nextInt();
int E = sc.nextInt();
for (int i = 1; i <= V; i++) {
parent[i] = i; // 부모 테이블 자기 자신으로 초기화
}
for (int i = 1; i <= E; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
// 각 간선마다 유니온 연산
union_parent(parent, a, b);
}
}
프림 알고리즘
시작 정점을 기준으로 가장 작은 간선과 연결된 정점을 선택하여 신장 트리 확장
Heap 이용
정점 선택 기반
그래프를 인접 리스트로 표현
순서
임의의 간선 선택
선택한 간선의 정점으로부터 가장 낮은 가중치를 갖는 정점 선택
모든 정점이 선택될 때까지 반복
// O(N^2) 알고리즘 (배열 이용)
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
int[][] input = new int[N][N];
int[] minEdge = new int[N];
boolean[] V = new boolean[N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
input[i][j] = sc.nextInt();
}
minEdge[i] = Integer.MAX_VALUE;
}
int minVertex, min, result = 0;
minEdge[0] = 0;
for (int c = 0; c < N; c++) {
min = Integer.MAX_VALUE;
minVertex = 0;
// priority queue로 대체 가능
// V와 연결된 간선 중 비용이 최소인 간선, 그 간선과 연결된 노드를 찾는다
for (int i = 0; i < N; i++){
if (!V[i] && min > minEdge[i]) {
min = minEdge[i];
minVertex = i;
}
}
// 해당 노드를 V에 포함시킨다.
result += min;
V[minVertex] = true;
for (int i = 0; i < N; i++) {
// V에서 해당 노드까지 가는 최소 비용 간선 갱신
if (!V[i] && input[minVertex][i] != 0 && minEdge[i] > input[minVertex][i]) {
minEdge[i] = iput[minVertex][i];
}
}
}
System.out.println(result);
}
// O(E log V) 알고리즘 (Priority Queue 이용)
static class Vertex implements Comparable<Vertex> {
int n, edge;
public Vertex(int n, int edge) {
super();
this.n = n;
this.edge = edge;
}
@Override
public int compareTo(Vertex o) {
return this.edge - o.edge;
}
}
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
int[][] input = new int[N][N];
int[] minEdge = new int[N];
PrioirtyQueue<Vertex> queue = new PriorityQueue<>();
boolean[] V = new boolean[N];
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
input[i][j] = sc.nextInt();
}
minEdge[i] = Integer.MAX_VALUE;
}
int result = 0, nodeCount = 0;
// 임의의 노드(0번 노드)를 포함시킨다
minEdge[0] = 0;
queue.offer(new Vertex(0, 0));
while (!queue.isEmpty()) {
Vertex minVertex = queue.poll();
if (V[minVertex.n]) continue;
result += minVertex.edge;
V[minVertex.n] = true;
if(++nodeCount == N) break;
for (int to = 0; to < N; to++) {
if (!V[to] && input[minVertex.n][to] != 0 && minEdge[to] > input[minVertex.n][to] {
minEdge[to] = input[minVertex.n][to];
queue.offer(new Vertex(to, inut[minVerted.n][to]));
}
}
}
System.out.println(result);
}
Kruskal vs Prim
프림은 사이클 가능성이 없음
크루스칼은 사이클이 이루어지는지 항상 확인 (find(a) == find(b))
간선의 개수가 적은 경우 크루스칼
간선의 개수가 많은 경우 프림