etc

[알고리즘] MST

오늘도개발 2024. 10. 21. 17:05

 

1. MST 란? 

최소 신장 트리는 연결 그래프의 부분 그래프로, 그래프의 모든 정점을 포함하면서 간선의 총 가중치가 최소가 되는 트리

( 네트워크 설계, 클러스터링, 그래픽스 등 사용 )

 

  • 연결 그래프: 모든 정점이 서로 연결되어 있는 그래프
  • 트리: 사이클이 없는 그래프
  • 신장 트리: 그래프의 모든 정점을 포함하는 트리
  • 최소 신장 트리: 신장 트리 중 간선 가중치의 합이 최소인 트리

 

2. 구현 방법

 

1. 크루스칼 알고리즘

  - 모든 간선을 가중치 기준으로 정렬합니다.

 

  - 가장 작은 가중치의 간선부터 차례대로 선택하여 MST에 추가

 

  - 간선을 추가할 때, 사이클이 발생하지 않도록 유니온-파인드(Union-Find) 자료구조를 사용하여 관리

 

시간 복잡도: O(E log E), E: 간선의 수

장점: 구현이 간단하고, 간선의 수가 적을 때 효율적

 

import java.util.*;

class Edge implements Comparable<Edge> {
    int src, dest, weight;

    public int compareTo(Edge edge) {
        return this.weight - edge.weight;
    }
}

class DisjointSet {
    int[] parent, rank;

    public DisjointSet(int n) {
        parent = new int[n];
        rank = new int[n];
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            rank[i] = 0;
        }
    }

    public int find(int u) {
        if (u != parent[u]) {
            parent[u] = find(parent[u]);
        }
        return parent[u];
    }

    public void union(int u, int v) {
        int rootU = find(u);
        int rootV = find(v);
        if (rootU != rootV) {
            if (rank[rootU] > rank[rootV]) {
                parent[rootV] = rootU;
            } else if (rank[rootU] < rank[rootV]) {
                parent[rootU] = rootV;
            } else {
                parent[rootV] = rootU;
                rank[rootU]++;
            }
        }
    }
}

public class KruskalMST {
    public static void main(String[] args) {
        int V = 4;
        Edge[] edges = new Edge[] {
            new Edge(0, 1, 10),
            new Edge(0, 2, 6),
            new Edge(0, 3, 5),
            new Edge(1, 3, 15),
            new Edge(2, 3, 4)
        };

        Arrays.sort(edges);

        DisjointSet ds = new DisjointSet(V);
        List<Edge> mst = new ArrayList<>();

        for (Edge edge : edges) {
            int u = edge.src;
            int v = edge.dest;

            if (ds.find(u) != ds.find(v)) {
                ds.union(u, v);
                mst.add(edge);
            }
        }

        System.out.println("최소 신장 트리의 간선:");
        for (Edge edge : mst) {
            System.out.println(edge.src + " -- " + edge.dest + " == " + edge.weight);
        }
    }
}

 

 

2. 프림 알고리즘

  - 시작 정점을 선택하여 MST에 포함

 

  - MST에 포함된 정점과 연결된 간선 중 가중치가 가장 작은 간선을 선택하여 MST에 추가

 

  - 모든 정점이 MST에 포함될 때까지 반복

 

시간 복잡도: O(E log V), V : 정점의 수 (우선순위 큐를 사용하면 더욱 효율적으로 구현 가능)

장점: dense graph(간선이 많은 그래프)에서 효과적

 

import java.util.*;

class Edge {
    int dest, weight;

    public Edge(int dest, int weight) {
        this.dest = dest;
        this.weight = weight;
    }
}

public class PrimMST {
    public static void main(String[] args) {
        int V = 5; 
        List<List<Edge>> graph = new ArrayList<>();

        for (int i = 0; i < V; i++) graph.add(new ArrayList<>());
        

        // 양방향 간선 추가
        graph.get(0).add(new Edge(1, 2));
        graph.get(1).add(new Edge(0, 2)); 
        graph.get(0).add(new Edge(3, 6));
        graph.get(3).add(new Edge(0, 6)); 
        graph.get(1).add(new Edge(2, 3));
        graph.get(2).add(new Edge(1, 3)); 
        graph.get(1).add(new Edge(3, 8));
        graph.get(3).add(new Edge(1, 8)); 
        graph.get(1).add(new Edge(4, 5));
        graph.get(4).add(new Edge(1, 5));
        graph.get(2).add(new Edge(4, 7));
        graph.get(4).add(new Edge(2, 7)); 
        graph.get(3).add(new Edge(4, 9));
        graph.get(4).add(new Edge(3, 9));

        primMST(graph, V);
    }

    public static void primMST(List<List<Edge>> graph, int V) {
        boolean[] inMST = new boolean[V]; // MST에 포함된 정점 여부
        Edge[] edgeTo = new Edge[V];
        int[] minWeight = new int[V]; // 최소 가중치
        Arrays.fill(minWeight, Integer.MAX_VALUE); // 초기화
        minWeight[0] = 0;

        PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
        pq.offer(new int[]{0, 0});

        while (!pq.isEmpty()) {
            int[] current = pq.poll();
            int u = current[0];

            if (inMST[u]) continue;
            inMST[u] = true; 

            for (Edge edge : graph.get(u)) {
                int v = edge.dest;
                int weight = edge.weight;

                if (!inMST[v] && weight < minWeight[v]) {
                    minWeight[v] = weight;
                    edgeTo[v] = edge;
                    pq.offer(new int[]{v, weight});
                }
            }
        }

        // 결과 출력
        System.out.println("최소 신장 트리의 간선:");
        for (int i = 1; i < V; i++) {
            if (edgeTo[i] != null) {
                System.out.println(edgeTo[i].dest + " -- " + (edgeTo[i].weight) + " (가중치: " + minWeight[i] + ")");
            }
        }
    }
}

 

'etc' 카테고리의 다른 글

[JavaScript] JSONView 브라우저에서 Json 표기 변환  (0) 2024.01.24
Git 사용법  (0) 2023.04.26
[MAC OS] Docker Download 및 설치  (0) 2022.08.06