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

Saemi Min·2023년 3월 3일
0

Data Structure & Algorithm

목록 보기
13/17
post-thumbnail

Spanning Tree

개념

그래프 내의 모든 정점을 포함하는 트리

= 신장 트리
: 최소 연결 부분 그래프

  • 최소 연결 = 간선 수가 가장 적다.
  • n개의 정점을 가지는 그래프의 최소 간선의 수는 (n-1)개이고, (n-1)개의 간선으로 연결되어 있으면 필연적으로 트리 형태가 되고 이것이 Spanning Tree가 된다.
  • 즉, 그래프에서 일부 간선을 선택해서 만든 트리

특징

  • DFS, BFS를 이용하여 그래프에서 신장 트리를 찾을 수 있다.
    탐색 도중에 사용된 간선만 모으면 만들 수 있다.
  • 하나의 그래프에는 많은 신장 트리가 존재할 수 있다.
  • Spanning Tree는 트리의 특수한 형태이므로 모든 정점들이 연결되어 있어야 하고 사이클을 포함해서는 안된다.
  • 따라서 Spanning Tree는 그래프에 있는 n개의 정점을 정확히 (n-1)개의 간선으로 연결한다.

MST (Minimum Spanning Tree)

개념

Spanning Tree 중에서 사용된 간선들의 가중치 합이 최소인 트리

  • 각 간선의 가중치가 동일하지 않을 때 단순히 가장 적은 간선을 사용한다고 해서 최소 비용이 얻어지는 것은 아니다.
  • MST는 간선에 가중치를 고려하여 최소 비용의 Spanning Tree를 선택하는 것을 말한다.
  • 즉, 네트워크(가중치를 간선에 할당한 그래프)에 있는 모든 정점들을 가장 적은 수의 간선과 비용으로 연결하는 것이다.

특징

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

구현 방법

1. Kruskal MST 알고리즘

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

  • 간선 선택을 기반으로 하는 알고리즘
  • 이전 단계에서 만들어진 신장 트리와는 상관없이 무조건 최소 간선만을 선택하는 방법이다.

과정

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

다음 간선을 이미 선택된 간선들의 집합에 추가할 때 사이클을 생성하는지 체크!!
-> 즉, 추가할 새로운 간선의 양끝 정점이 같은 집합에 속해 있으면 사이클이 형성된다.

사이클 생성 여부를 확인하는 방법
: 추가하고자 하는 간선의 양끝 정점이 같은 집합에 속해 있는지를 먼저 검사해야 한다.
: union-find 알고리즘 이용

import sys

v, e = map(int, input().split())

# 부모 테이블 초기화
parent=[i for i in (1, v+1)]

# find 연산
def find(x):
    if parent[x] !=x:
        parent[x]= find(parent[x])
    return parent[x]

def union(a, b):
    a=find(a)
    b=find(b)
    if a<b:
        parent[b]=a
    else:
        parent[a] =b

# 간선 정보 담을 리스트와 최소 신장 트리 계산 변수 정의
edges=[]
total_cost=0

# 간선 정보 주어지고 비용을 기준으로 정렬
for _ in range(e):
    a, b, c = map(int, input().split())
    edges.append((c, a, b))

# 간선 정보 비용 기준으로 오름차순 정렬
edges.sort()


# 간선 정보 하나씩 확인하면서 크루스칼 알고리즘 수행
for i in range(e):
    c, a, b = edges[i]
    # find 연산 후, 부모 노드 다르면 사이클 발생하지 않으므로 union 연산 수행 -> 최소 신장 트리에 포함!
    if find(a) != find(b):
        union(a, b)
        total_cost+=total_cost

print(total_cost)

시간 복잡도

  • union-find 알고리즘을 이용하면 Kruskal 알고리즘의 시간 복잡도는 간선들을 정렬하는 시간에 좌우된다.
  • 즉, 간선 e개를 퀵 정렬과 같은 효율적인 알고리즘으로 정렬한다면 Kruskal 알고리즘의 시간 복잡도는 O(elog₂e) 이 된다.

그래프 내에 적은 숫자의 간선만을 가지는 ‘희소 그래프(Sparse Graph)’의 경우 Kruskal 알고리즘이 적합


2. Prim MST 알고리즘

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

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

과정

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

시간 복잡도

  • 주 반복문이 정점의 수 n만큼 반복하고, 내부 반복문이 n번 반복
    Prim의 알고리즘의 시간 복잡도는 O(n^2)이 된다.

그래프에 간선이 많이 존재하는 ‘밀집 그래프(Dense Graph)’ 의 경우는 Prim 알고리즘이 적합


참고 사이트

profile
I believe in myself.

0개의 댓글