본문 바로가기

TIL

23.10.11

MST(Minimum Spanning Tree)

📢 목차

  1. 그래프
  2. 신장트리 (Spanning Tree)
  3. 최소신장트리 (Minimum Spanning Tree)

1. 그래프

📖 그래프 (Graph)

  • 연결되어 있는 원소간의 관계를 표현한 자료구조
  • 연결할 객체를 표현하는 정점(Vertex)와 객체를 연결하는 간선(Edge)로 구성된 집합

1-1. 종류

  • 무방향 그래프
    • 간선에 방향이 없는 그래프
  • 방향 그래프
    • 간선에 방향이 있는 그래프
  • 완전 그래프
    • 한 정점에서 모든 정점에 연결되어 최대 간선 수를 갖는 그래프
  • 부분 그래프
    • 완전그래프가 아닌 그래프
  • 연결 그래프
    • 떨어져있는 정점이 없는 그래프
  • 단절 그래프
    • 연결 그래프가 아닌 그래프
  • 가중 그래프
    • 간선에 가중치가 있는 그래프
  • 유향 비순환 그래프(DAG)
    • 사이클이 없는 방향 그래프

1-2. 용어

  • 차수 : 정점에 부속되어있는 간선의 수
    • 진입차수 : 정점을 머리로하는 간선의 수
    • 진출차수 : 정점을 꼬리로 하는 간선의 수
  • 경로 : 정점 $V_i$에서 정점$V_j$까지 간선으로 연결되는 정점을 순서대로 나열한 리스트
    • 단순경로 : 서로 다른 정점으로 연결된 경로
  • 사이클 : 단순경로중 시작 정점과 마정점이 같은 경로

1-3. 구현

  • 🔽 인접 행렬
    • 정점의 개수를 V라고 했을 때, V x V 크기의 2차원 배열을 이용
    • 정점의 연결관계를 0과 1로 표현
    • 장점
      • 정점 V1와 정점 V2의 연결 여부를 O(1)에 알수 있다.
    • 단점
      • 실제 연결된 정점의 수와 관계없이 연결된 정점을 찾는데 O(n)이 소요됨
      public class Graph
      {
        private int[,] adjMatrix;
      
        public Graph(int N)
        {
            adjMatrix = new int[N, N];
        }
      
        // 무방향 그래프일 경우
        public void insert(int i, int j)
        {
            adjMatrix[i,j] = 1;
            adjMatrix[j,i] = 1;
        }
      
        // 방향 그래프일 경우 i -> j
        public void insert(int i, int j)
        {
            adjMatrix[i,j] = 1;
        }
      
        // i와 j가 연결되어있는지 여부
        public bool isConnect(int i, int j)
        {
            return adjMatrix[i,j] == 1;
        }
      
        // 정점 v의 간선의 수
        public int getEdge(int v)
        {
            int cnt = 0, length = adjMatrix.getLength(0);
      
            for(int i = 0; i< length ; i++)
            {
                for(int j= 0; j  <= i); j++)
                {
                    if(adjMatrix[i,j] == 1)
                        cnt++;
                }
            }
      
            return cnt;
        }
      }
  • 🔽 인접 리스트
    • 각각의 정점에 대하여 인접한 정점 번호를 저장
    • 링크드 리스트를 이용해서 구현
    • A[i] = i 와 연결된 정점을 링크드 리스트로 포함하고 있음
    • 장점
      • 정점과 연결된 정점을 찾는데 필요한 만큼만 시간이 소요됨
    • 단점
      • 정점 V1와 정점 V2의 연결 여부를 알기위해서 연결된 정점을 모두 살펴봐야한다.
      public class Graph
      {
        private LinkedList<int>[] adjList;
      
        public Graph(int N)
        {
            adjList= new LinkedList<int>[N];
      
            for(int i = 0; i < N; i++)
            {
                adjList[i] = new LinkedList<int>();
            }
        }
      
        // 무방향 그래프일 경우
        public void insert(int i, int j)
        {
            adjList[i].AddLast(j);
            adjList[j].AddLast(i)
        }
      
        // 방향 그래프일 경우 i -> j
        public void insert(int i, int j)
        {
            adjList[i].AddLast(j)
        }
      
        // i와 j가 연결되어있는지 여부
        public bool isConnect(int i, int j)
        {
            foreach(int n in adjList[i])
            {
                if(j == n)
                    return true;
            }
      
            return false;
        }
      
        // 정점 v의 간선의 수
        public int getEdge(int v)
        {
            return adjList[v].Count;
        }
      }

2. 신장트리(Spanning Tree)

📖 **신장트리 (Spanning Tree)**

  • 그래프 내의 모든 정점을 포함하는 트리
  • 그래프의 최소 연결 부분 그래프
    • 최소 연결 = 연결 그래프 중에 간선의 수가 최소이다.
    • n개의 정정을 가지고 있는 연결 그래프의 최소 간선의 수는 (n-1)

2-1. 특징

  • DFS, BFS을 이용하여 그래프에서 신장 트리를 찾을 수 있다
  • 하나의 그래프에는 여러 개의 신장트리가 존재할 수 있다.
  • 모든 정점들이 연결되어 있어야 하고, 사이클이 포함되어서는 안된다.

3. 최소신장트리(Minimum Spanning Tree)

📖 최소신장트리 (Minimum Spanning Tree)

  • 신장트리에서 사용된 간선들의 가중치 합이 최소인 트리
  • 간선들의 가중치가 동일하지 않을 때 단순히 가장 적은 간선을 사용한다고 해서 최소비용이 얻어지지 않는다.
  • 객체를 연결할 때 비용이 최소로 되는 경우에 사용된다.

3-1. 특징

  • 간선의 가중치의 합이 최소여야 한다.
  • n개의 정점을 가지는 그래프에 대해 반드시 (n-1)개의 간선만을 사용해야 한다.
  • 사이클이 포함되어서는 안된다.

3-2. 구현

1️⃣ Kruskal MST 알고리즘

탐욕적인 방법(greedy method) 을 이용하여 네트워크(가중치를 간선에 할당한 그래프)의 모든 정점을 최소 비용으로 연결하는 최적 해답을 구하는 것

  • MST(최소 비용 신장 트리) 가 1) 최소 비용의 간선으로 구성됨 2) 사이클을 포함하지 않음 의 조건에 근거하여 각 단계에서 사이클을 이루지 않는 최소 비용 간선을 선택 한다.
  • 간선 선택을 기반으로 하는 알고리즘이다.
  • 이전 단계에서 만들어진 신장 트리와는 상관없이 무조건 최소 간선만을 선택하는 방법이다.

의사코드(Pseudo Code)

  1. 그래프의 간선들을 가중치의 오름차순으로 정렬한다.
  2. 정렬된 간선 리스트에서 순서대로 사이클을 형성하지 않는 간선을 선택한다.
    1. 즉, 가장 낮은 가중치를 먼저 선택한다.
    2. 사이클을 형성하는 간선을 제외한다
  3. .해당 간선을 현재의 MST(최소 비용 신장 트리)의 집합에 추가한다.

 

  • 🔽 UnionFind.cs 코드
using System;

public class Edge : IComparable<Edge>
{
    public int from { get; private set; }
    public int to { get; private set; }
    public int weight { get; private set; }

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

    public int CompareTo(Edge other)
    {
        if (weight < other.weight)
            return -1;
        if (weight > other.weight)
            return 1;
        return 0;
    }
}
  • 🔽 UnionFind.cs 코드
public class UnionFind
{
    int[] parents;

    public UnionFind(int N)
    {
        parents = new int[N];
    }

    public int Find(int x)
    {
        if (parents[x] == x) return x;
        return parents[x] = Find(parents[x]);
    }

    public bool isUnion(int a, int b)
    {
        a = Find(a);
        b = Find(b);
        if (a == b) return true;
        else return false;
    }

    public void Union(int a, int b)
    {
        a = Find(a);
        b = Find(b);
        if (a < b) parents[b] = a;
        else parents[a] = b;
    }
}
  • 🔽 Kruskal.cs 코드
using System;
using System.Collections.Generic;

public class Kruskal
{
    public Kruskal(int N, string[] input)
    {
        List<Edge> edges = new List<Edge>();
        UnionFind uf = new UnionFind(N);

        foreach (string str in input)
        {
            string[] split = str.Split(" ");

            int from = Convert.ToInt32(split[0]);
            int to = Convert.ToInt32(split[1]);
            int weight = Convert.ToInt32(split[2]);

            edges.Add(new Edge(from, to, weight));
        }

        edges.Sort();

        int sum = 0;

        for (int i = 0; i < edges.Count; i++)
        {
            if (!uf.isUnion(edges[i].from, edges[i].to))
            {
                sum += edges[i].weight;
                uf.Union(edges[i].from, edges[i].to);
            }
        }
	
			Console.WriteLine(sum);
    }
}

 

2️⃣ Prim MST 알고리즘

시작 정점에서부터 출발하여 신장트리 집합을 단계적으로 확장 해나가는 방법

  • 정점 선택을 기반으로 하는 알고리즘이다.
  • 이전 단계에서 만들어진 신장 트리를 확장하는 방법이다.

의사코드(Pseudo Code)

  1. 시작 단계에서는 시작 정점만이 MST(최소 비용 신장 트리) 집합에 포함된다.
  2. 앞 단계에서 만들어진 MST 집합에 인접한 정점들 중에서 최소 간선으로 연결된 정점을 선택하여 트리를 확장한다.
    1. 즉, 가장 낮은 가중치를 먼저 선택한다.
  3. 위의 과정을 트리가 (N-1)개의 간선을 가질 때까지 반복한다.

 

  •  
  • 🔽 Edge.cs 코드
public class Edge
{
	public int to;
	public int weight;

	public Edge(int to, int weight)
	{
		this.to = to;
		this.weight = weight;
	}
}
  • 🔽 Graph.cs 코드
using System.Collections.Generic;

public class Graph
{
	List<Edge>[] edges;

	public Graph(int N)
	{
		edges = new List<Edge>[N];

		for(int i = 0; i < N; i++)
		{
			edges[i] = new List<Edge>();
		}
	}

	public void insert(int from, int to, int weight)
	{
		edges[from].Add(new Edge(to, weight));
	}

	public List<Edge> get(int v)
	{
		return edges[v];
	}
}
  • 🔽 PrimMST.cs 코드
using System;
using System.Collections.Generic;

public class PrimMST
{
    public PrimMST(int N, string[] input, int start)
    {
        Graph graph = new Graph(N);

        foreach (string str in input)
        {
            string[] split = str.Split(" ");

            int from = Convert.ToInt32(split[0]);
            int to = Convert.ToInt32(split[1]);
            int weight = Convert.ToInt32(split[2]);

            graph.insert(from, to, weight);
        }

        PriorityQueue<Edge, int> pq = new PriorityQueue<Edge, int>();

        bool[] isVisited = new bool[N];

        pq.Enqueue(new Edge(start, 0), 0);

        int sum = 0;

        while (pq.Count != 0)
        {
            Edge edge = pq.Dequeue();
            int v = edge.to;
            int weight = edge.weight;

            if (isVisited[v])
                continue;

            isVisited[v] = true;
            sum += weight;

            List<Edge> edges = graph.get(v);

            foreach (Edge e in edges)
            {
                if (!isVisited[e.to])
                    pq.Enqueue(e, e.weight);
            }
        }

        Console.WriteLine(sum);
    }
}

 

4. 예제 문제

4-1. 연습문제

1197번: 최소 스패닝 트리

1922번: 네트워크 연결

4-2 실전 문제

1368번: 물대기

1045번: 도로

17472번: 다리 만들기 2

'TIL' 카테고리의 다른 글

23.10.13  (0) 2023.10.17
23.10.12  (0) 2023.10.12
23.10.10  (0) 2023.10.10
23.10.06  (0) 2023.10.06
23.10.05  (0) 2023.10.05