Algorithm/Theory

[ 그래프 MST ] 크루스칼 알고리즘

youth787 2023. 9. 24. 16:11

신장 트리 : 그래프의 모든 정점과 간선의 부분 집합으로 구성되는 트리. 신장 트리는 여러가지. 

 

최소 신장 트리 : 신장 트리 중에서 사용된 간선들의 가중치 합이 최소인 트리. 최소 신장 트리도 여러가지 나올 수 있다. 

특징 : 무방향 가중치 그래프, 사이클을 포함해서는 안된다. N개의 정점을 가지는 그래프에 대해 반드시 (N-1)개의 간선을 사용해야 한다. 

 

크루스칼 알고리즘 

간선을 하나씩 선택해서 최소 신장 트리를 찾는 알고리즘 

1. 모든 간선을 가중치에 따라 오름차순 정렬

2. 가중치가 낮은 간선부터 선택하면서 트리를 증가시킨다. 

- 사이클이 존재하면 다음으로 가중치가 낮은 간선을 선택한다.

3. n-1개의 간선이 선택될때까지 2를 반복. 

 

* 슈도 코드 

 

MST-KRUSKAL(G,w)
	A <- 0 // 0 : 공집합 
    For vertex v in G.V // G.V : 그래프의 정점 집합 
     	Make_Set(v) // G.E : 그래프의 간선 집합 
        
    G.E에 포함된 간선들을 가중치 w에 의해 정렬
    
    For 가중치가 가장 낮은 간선 G.E와 연결된 (u,v)선택 (n-1)개
    	If Find_Set(u) != Find_Set(v)
        A <- A 합 {(u,v)}
        Union(u,v);
        
    RETURN A

 

 

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

public class 크루스칼 {

	static int[] p; // 부모를 저장할 배열

	public static void main(String[] args) {
		Scanner sc = new Scanner(input);

		int V = sc.nextInt(); // 정점의 개수 0부터 시작한다.
		int E = sc.nextInt(); // 간선의 수

		// 간선배열을 만들어서 저장 (사용자 정의 클래스 / 2차원 배열)
		// [0]:시작정점 [1]:끝정점 [2]:가중치)
		int[][] edges = new int[E][3];

		for (int i = 0; i < E; i++) {
			edges[i][0] = sc.nextInt();
			edges[i][1] = sc.nextInt();
			edges[i][2] = sc.nextInt();
		} // 입력끝

		// 크루스칼 1단계 : 간선을 정렬(오름차순)
		Arrays.sort(edges, new Comparator<int[]>() {
			@Override
			public int compare(int[] o1, int[] o2) {
				return o1[2] - o2[2];
			}
		});

		// 크루스칼 2단계 : 사이클이 발생안하도록 V-1개의 간선을 뽑는다.
		p = new int[V];
		// 2-1. make-set
		for (int i = 0; i < V; i++) {
			makeset(i);
		}

		// 2-2. 검사하면서 뽑기.
		int ans = 0; // 최소비용을 저장할 변수.
		int pick = 0; // 뽑은 간선의 수를 저장할 변수.

		// while을 쓰면 edges 배열을 돌릴 index 변수가 하나 필요.
		// for문을 이용해서 쓰면 if문을 통해 break를 걸어줘야한다.(선택)
		for (int i = 0; i < E; i++) {
			int px = findset(edges[i][0]);
			int py = findset(edges[i][1]);

			if (px != py) { // 사이클이 형성 안되는 경우. 
				union(x, y);
				union(px, py);
				ans += edges[i][2];
				pick++;
			}
			if (pick == V - 1)
				break;
		}
		System.out.println(ans);
	}

	static void union(int x, int y) {
		p[findset(y)] = findset(x); 
	}

	static int findset(int x) {
		// pass compression 적용. 
		if (x != p[x])
			p[x] = findset(p[x]);
		return p[x];
	}

	static void makeset(int i) {
		p[i] = i;
	}

	static String input = "7 11\r\n" + "0 1 32\r\n" + "0 2 31\r\n" + "0 5 60\r\n" + "0 6 51\r\n" + "1 2 21\r\n"
			+ "2 4 46\r\n" + "2 6 25\r\n" + "3 4 34\r\n" + "3 5 18\r\n" + "4 5 40\r\n" + "4 6 51\r\n" + "\r\n";
}
반응형