MST(Minimum Spanning Tree)는 최소신장트리
또는 최소 비용 걸침 나무
라고 하는데, 그래프의 신장트리들 중 간선의 합이 최소인 신장트리를 의미한다.
1. 입력조건
'무향 연결 그래프'여야 한다.
2. 신장트리 = 걸침나무
Minimum Spanning Tree
라고 한다.Prim의 수도코드
먼저 Prim 알고리즘의 수도코드를 보자.
Prim(G, r)
{
S <- {}
정점 r을 방문했다고 표시하고 집합 S에 포함시킨다.
while (S != V){
S에서 V-S를 연결하는 간선들 중 최소길이의 간선(x, y)를 찾는다;
정점 y를 방문했다고 표시하고, 집합 S에 포함시킨다;
}
}
Prim의 이해
Prim의 특징
입력
위 사진과 동일한 가중치가 존재하는 그래프는 아래처럼 dictionary 안에 dictionary 형태로 표현할 수 있다.
mywgraph = { "A" : {"B":8, "C":11, "D":9},
"B" : {"A":11, "E":10},
"C" : {"A":11, "D":13, "F":8},
"D" : {"A":9, "C":13, "E":5, "G":12},
"E" : {"B":10, "D":5},
"F" : {"C":8, "G":7},
"G" : {"D":12, "F":7}
}
Prim 구현(Python)
def Prim(G, r) :
conn = [] # 간선이 추가된 순서
S = [r] # 방문한 노드 리스트
while len(S) != len(G):
xmin, ymin, min_value = find_min_edge(S, G) # 최소 간선 찾기
S.append(ymin)
conn.append([xmin, ymin, min_value])
return conn
def find_min_edge(S,G): # 최소 간선 찾기
edges = []
for x in S:
for y in G[x]:
if y not in S:
edges.append([x, y, G[x][y]])
xmin, ymin, min_value = min(edges, key = lambda x:x[-1])
return xmin, ymin, min_value
Prim(mywgraph, "A")
"""
[['A', 'B', 8],
['A', 'D', 9],
['D', 'E', 5],
['A', 'C', 11],
['C', 'F', 8],
['F', 'G', 7]]
"""
Kruskal의 수도코드
Kruskal(G)
{
T <- {}; // T는 신장트리
단 하나의 정점만으로 이루어진 n개의 집합을 S로 초기화 한다;
모든 간선을 가중치가 작은 순으로 정렬한다;
while (T의 간선수 < n-1){
최소비용 간선 (u, v)를 제거한다;
정점 u와 정점 v가 서로 다른 집합에 속하면{
두 집합을 하나로 합친다;
T <- T + {(u, v)};}
}
}
Kruskal의 이해
입력
위와 동일
Kruskal 구현(python)
def Kruskal(G):
T = []
S = [set(x) for x in G.keys()]
print("S=", S)
E = sort_edges(G)
while len(T) < len(G)-1:
edge, value = E.popitem() # 작은 맨 뒤 삭제
u, v = list(edge)
if any(u in subset and v in subset for subset in S) == False: # 정점 u와 v가 서로 다른 집합에 속하면
# u가 있는 집합과 v가 있는 집합 합치기
merged_set = set()
for s in S:
if u in s:
merged_set.update(s)
elif v in s:
merged_set.update(s)
S = [merged_set] + [s for s in S if u not in s and v not in s]
print("S=", S)
T.append([set(edge), value])
return T
def sort_edges(G):
E = {}
for k1,v1 in G.items():
for k2,v2 in v1.items():
E[frozenset([k1,k2])] = v2
E = {k: v for k, v in sorted(E.items(), key=lambda item: item[1], reverse=True)}
return E # {frozenset({'C', 'D'}): 13, frozenset({'D', 'G'}): 12, frozenset({'B', 'A'}): 11, frozenset({'C', 'A'}): 11, frozenset({'E', 'B'}): 10, frozenset({'A', 'D'}): 9, frozenset({'F', 'C'}): 8, frozenset({'F', 'G'}): 7, frozenset({'E', 'D'}): 5}
print(Kruskal(mywgraph))
"""
S= [{'A'}, {'B'}, {'C'}, {'D'}, {'E'}, {'F'}, {'G'}]
S= [{'E', 'D'}, {'A'}, {'B'}, {'C'}, {'F'}, {'G'}]
S= [{'F', 'G'}, {'E', 'D'}, {'A'}, {'B'}, {'C'}]
S= [{'F', 'C', 'G'}, {'E', 'D'}, {'A'}, {'B'}]
S= [{'E', 'A', 'D'}, {'F', 'C', 'G'}, {'B'}]
S= [{'E', 'B', 'A', 'D'}, {'F', 'C', 'G'}]
S= [{'E', 'C', 'A', 'F', 'B', 'D', 'G'}]
[[{'E', 'D'}, 5], [{'F', 'G'}, 7], [{'F', 'C'}, 8], [{'A', 'D'}, 9], [{'E', 'B'}, 10], [{'C', 'A'}, 11]]
"""
frozenset을 쓰는 이유?
아래처럼 key는 간선, value는 가중치로 표현하는 dictionary를 만들고 싶다.
di = {{'D', 'E'} = 2,
{'D', 'A'} = 3}
근데 아래처럼 set을 key로 쓰게 되면 오류가 난다.
di = {}
di[{'D', 'E'}] = 2
di
# TypeError: unhashable type: 'set'
그래서 frozenset을 key로 쓰는 것이다.
di = {}
di[frozenset(['D', 'E'])] = 2
di # {frozenset({'D', 'E'}): 2}