최소 신장 트리
최소 신장트리
-
사용한 간선들의 가중치 합이 최소
-
간선 가중치를 고려하여 최소 비용의 신장 트리 선택
-
그래프의 모든 노드 포함
-
모든 노드는 서로 연결
-
사이클 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;}@Overridepublic 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))
-
간선의 개수가 적은 경우 크루스칼
-
간선의 개수가 많은 경우 프림