STUDY/ALGO | DS

[알고리즘] MST (최소 신장 트리, Minimum Spanning Tree)

0298 2021. 8. 14. 21:18

MST (최소 신장 트리, Minimum Spanning Tree)

 

 

1. MST

그래프의 모든 정점을 연결할 때, 모든 정점을 간선으로 연결하며 싸이클은 존재하지 않고, 연결된 간선들의 가중치의 합은 최소가 되는 것을 말한다.

 

+) Spanning Tree : 그래프 내 모든 정점을 포함하는 트리

+) 정점 : V ,  간선 : E

 

 

2. Kruskal's Algorithm | 크루스칼 알고리즘

- 간선 선택 기반

- 시간복잡도 : O(ElogV)

    ㄴ 간선 정렬 : O(ElogE)

    ㄴ Unifon Find : O(1) * E = O(E)

    O(ElogE + E) => O(ElogV^2),  (E <= V^2, fully connected graph) => O(ElogV)

 

 

1) 간선 리스트의 가중치들을 오름차순으로 정렬

2) 가중치가 작은 간선 선택

3) union-find를 이용하여 부모가 같지 않으면 연결 (싸이클이 생기지 않도록)

 

 

+) Union-Find

https://void2017.tistory.com/304

 

[알고리즘] Union-Find (유니온 파인드)

Union-Find (유니온 파인드) 1. Union-Find - 합집합 찾기 알고리즘 - Disjoint-Set, 서로소 집합 이라고도 한다. - 2개의 서로 다른 노드(x, y)를 선택했을 때, 2개의 노드가 같은 집합(그래프)에 속하는지 확인

void2017.tistory.com

 

 

예시 코드 - [프로그래머스] 섬 연결하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.util.*;
class Solution {
   static int[] parent;
    static int find(int x) {
        if(x == parent[x]) return x;
        return parent[x] = find(parent[x]);
    }
 
    static void union(int x, int y) { // y > x
        x = find(x);
        y = find(y);
 
        if(x != y) parent[y] = x;
    }
 
    static int solution(int n, int[][] costs) {
        int answer = 0;
        // 1) 간선 리스트의 가중치를 기준으로 오름차순 정렬
        Arrays.sort(costs, Comparator.comparingInt(o -> o[2]));
 
        // union-find (parent[] init)
        parent = new int[n];
        for(int i = 0; i < n; i++) parent[i] = i;
 
        // 2) 가중치가 작은 간선부터 선택
        for(int i = 0; i < costs.length; i++) {
            int start = costs[i][0];
            int end = costs[i][1];
            int weight = costs[i][2];
 
            // 3) 부모노드가 같지 않은 경우 연결
            if(find(start) != find(end)) {
                if(start > end) {
                    int tmp = end;
                    end = start;
                    start = tmp;
                }
                union(start, end);
                answer += weight;
            }
        }
 
        return answer;
    }
}
cs

+) 간선이 적으면 크루스칼!

 

 

3. Prim's Algorithm | 프림 알고리즘

- 정점 선택 기반 

- 시간복잡도 : O(ElogV) - 이진 힙 // 배열을 사용한다면 O(V^2)

    ㄴ 모든 정점 우선 순위 큐로 확인 : O(logV)

    ㄴ 간선 확인 : O(E)

    ㄴ O(ElogV)

 

 

1) 임의의 정점을 하나 선택

2) 정점에서 가중치가 낮은 간선을 선택

3) 모든 정점을 방문할 때까지 반복

 

 

예시 코드 - [프로그래머스] 섬 연결하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.util.*;
class Solution {
 static class Edge {
        int target;
        int weight;
 
        public Edge(int target, int weight) {
            this.target = target;
            this.weight = weight;
        }
    }
    static int solution(int n, int[][] costs) {
       int answer = 0;
        // 가중치 오름차순 정렬
        PriorityQueue<Edge> pq = new PriorityQueue<>((o1, o2) -> o1.weight - o2.weight);
        List<List<Edge>> list = new ArrayList<>();
 
        // init
        for(int i = 0; i <= n; i++) list.add(new ArrayList<>());
 
        for(int i = 0; i < costs.length; i++) {
            list.get(costs[i][0]).add(new Edge(costs[i][1], costs[i][2]));
            list.get(costs[i][1]).add(new Edge(costs[i][0], costs[i][2]));
        }
 
        boolean[] vtd = new boolean[n];
 
        //임의의 정점 하나 선택
        pq.add(new Edge(0,0));
        
        while(!pq.isEmpty()) {
            Edge e = pq.poll();
            if(vtd[e.target]) continue;
            vtd[e.target] = true;
            answer += e.weight;
 
            for(Edge edge: list.get(e.target)) {
                if(!vtd[edge.target]) pq.add(new Edge(edge.target, edge.weight));
            }
        }
 
        return answer;
    }
 
}
cs

+) 간선이 많으면 프림!

 

 

4. 참고 문제

1) [백준] 최소 스패닝 트리

https://void2017.tistory.com/100

 

[백준] 1197 최소 스패닝 트리

www.acmicpc.net/problem/1197 1197번: 최소 스패닝 트리 첫째 줄에 정점의 개수 V(1 ≤ V ≤ 10,000)와 간선의 개수 E(1 ≤ E ≤ 100,000)가 주어진다. 다음 E개의 줄에는 각 간선에 대한 정보를 나타내는 세 정..

void2017.tistory.com

 

2) [프로그래머스] 섬 연결하기

https://void2017.tistory.com/306

 

[프로그래머스] 섬 연결하기

https://programmers.co.kr/learn/courses/30/lessons/42861# 코딩테스트 연습 - 섬 연결하기 4 [[0,1,1],[0,2,2],[1,2,5],[1,3,1],[2,3,8]] 4 programmers.co.kr 2021-08-14 MST 알고리즘 1. 크루스칼 알고리즘 1..

void2017.tistory.com