자료구조 & 알고리즘/최소신장트리 - 프루스칼
최소신장트리 - 크루스칼 구현
꾸준함의 미더덕
2024. 9. 9. 21:47
[크루스칼 알고리즘]
간선을 가중치 순으로 오름차순 정렬하고, 사이클을 검사하면서 최소 신장 트리를 구성하는 간선 중심 알고리즘.
간선을 양방향으로 저장할 필요 없이 단방향만 저장해도 충분.
사이클 체크 방법 -> 유니온 파인드
주로 간선 수가 적을 때 사용
O(ElogE) -> 간선 만큼의 정렬 복잡도를 따라감
cf) 크루스칼, 프림 모두무방향 그래프를 대상
-> 프림은 노드 중심 알고리즘이기 때문에 간선 양방향 저장
public class Main {
static int parents[];
public static int example(int[][] data, int v, int e) {
int weightSum = 0;
parents = new int[v + 1];
for (int i = 0; i < parents.length; i++) {
parents[i] = i;
}
Arrays.sort(data, (x, y) -> x[2] - y[2]);
for (int i = 0; i < data.length; i++) {
// find의 결과가 다르면 사이클이 아님
// find의 결과는 각 요소의 그룹 루트 노드를 찾는 것..
if (find(data[i][0]) != find(data[i][1])) {
union(data[i][0], data[i][1]);
// 크루스칼 알고리즘은 가중치가 작은 간선부터 선택하는 순서를 통해 결과가 항상 최소가 되도록 보장
weightSum += data[i][2];
}
// 결과 같으면 사이클이라 넘김
}
return weightSum;
}
/**
* if (find(data[i][0]) != find(data[i][1]))
* 를 충족한 경우에 union(data[i][0], data[i][1]) 를 하는 것이므로
* 동일한지에 대한 판단이 필요없지 않을까 했는데
* (동일 판단은 이미 한 것이지만)
* find()의 역할은 동일한 *루트노드*를 찾아서 반환하는 것
* 따라서 합칠 때 루트 노드를 찾아야하는데,
* 단순히 parents[a] = bResult;는 루트노르를 찾지 않은 상태로 저장하는 것임 !!
*/
// public static void union(int a, int b) {
// int bResult = find(b);
// parents[a] = bResult; // 조건 없이 그냥 합쳐도 될거 같은데? -> 안됨 (집합의 루트를 찾아야함)
// }
/**
* 값의 대소 상관 없이 집합이 같다면 합침 -> 유니온 파인드는 대소관계 관계없이 사이클 방지 체크만 한다
* <p>
* - 유니온-파인드는 어떤 두 노드가 같은 집합에 속해 있는지 확인하고, 그렇지 않으면 집합을 합치는 작업만 담당
* - 즉, 유니온-파인드는 어떤 노드가 부모가 되는지 또는 어떤 순서로 합치는지와는 상관없이,
* - 두 노드가 같은 집합에 속했는지 여부만 확인하여 집합을 관리..
*/
public static void union(int a, int b) {
int aResult = find(a); // a 집합의 루트 노드 찾기
int bResult = find(b); // b 집합의 루트 노드 찾기
// 루트 노드를 찾았다면 동일 비교하지 않아도 됨! !!
// if (aResult != bResult) {
// parents[aResult] = bResult;
// }
parents[aResult] = bResult; // 합칠 때는 대소 관계 없음
}
public static int find(int target) {
//그룹의 최종 부모는 본인 인덱스 == 본인 값
if (parents[target] == target) {
return target;
}
//재귀로 찾아가서 최종 부모의 값 저장
parents[target] = find(parents[target]);
return parents[target];
}
public static void main(String[] args) {
// Test code
int v = 7;
int e = 10;
int[][] graph = {{1, 3, 1}, {1, 2, 9}, {1, 6, 8}, {2, 4, 13}, {2, 5, 2}, {2, 6, 7}, {3, 4, 12}, {4, 7, 17}, {5, 6, 5}, {5, 7, 20}};
System.out.println(example(graph, v, e));
}
}