from collections import defaultdict
from heapq import *
def prim(first_node, edges) :
mst = []
# 모든 간선의 정보를 adjacent_egdes에 저장
adjacent_edges = defaultdict(list)
for weight, node1, node2 in edges :
adjacent_edges[node1].append((weight, node1, node2))
adjacent_edges[node2].append((weight, node2, node1))
# 처음 선택한 노드를 연결된 노드 집합에 삽입
connected_nodes = set(first_node)
# 선택된 노드에 연결된 간선을 간선 리스트에 삽입
candidated_edge = adjacent_edges[first_node]
# heap 구조로 바꾸며, 가중치 기준 오름차순으로 정렬
heapify(candidated_edge)
while candidated_edge :
weight, node1, node2 = heappop(candidated_edge)
# 사이클 있는지 확인 후 연결
if node2 not in connected_nodes :
connected_nodes.add(node2)
mst.append((weight, node1, node2))
for edge in adjacent_edges[node2] :
if edge[2] not in connected_nodes :
heappush(candidated_edge, edge)
return mst
edges = [
(7, 'A', 'B'), (5, 'A', 'D'),
(8, 'B', 'C'), (9, 'B', 'D'), (7, 'B', 'E'),
(5, 'C', 'E'),
(15, 'D', 'E'), (6, 'D', 'F'),
(8, 'E', 'F'), (9, 'E', 'G'),
(11, 'F', 'G')
]
prim('A', edges)
# 결과 출력
# [(5, 'A', 'D'),
# (6, 'D', 'F'),
# (7, 'A', 'B'),
# (7, 'B', 'E'),
# (5, 'E', 'C'),
# (9, 'E', 'G')]
O(ElogE)
"""
heapdict을 쓰는 이유:
새롭게 heap에 push하거나 pop하지 않아도
기존의 heap 내용만 update한다 하더라도
알아서 최소 힙의 구조로 업데이트 함
* heapdict을 쓰기 전에 HeapDict 라이브러리 설치하기:
-> pip install HeapDict
"""
from heapdict import heapdict
def prim(graph, start) :
mst, keys, previous, total_weight = [], heapdict(), dict(), 0
for node in graph.keys() :
keys[node] = float('inf')
previous[node] = None
keys[start], previous[start] = 0, start
while keys :
current_node, current_key = keys.popitem()
mst.append([previous[current_node], current_node, current_key])
total_weight += current_key
for adjacent, weight in graph[current_node].items() :
if adjacent in keys and weight < keys[adjacent] :
keys[adjacent] = weight
previous[adjacent] = current_node
return mst, total_weight
graph = {
'A': {'B': 7, 'D': 5},
'B': {'A': 7, 'D': 9, 'C': 8, 'E': 7},
'C': {'B': 8, 'E': 5},
'D': {'A': 5, 'B': 9, 'E': 15, 'F': 6},
'E': {'B': 7, 'C': 5, 'F': 8, 'G': 9},
'F': {'D': 6, 'E': 8, 'G': 11},
'G': {'E': 9, 'F': 11}
}
mst, total_weight = prim(graph, 'A')
print(mst)
print(total_weight)
# 결과 출력
# [['A', 'A', 0], ['A', 'D', 5], ['D', 'F', 6], ['A', 'B', 7], ['B', 'E', 7], ['E', 'C', 5], ['E', 'G', 9]]
# 39
O(V)
O(VlogV)
O(logV)
O(ElogV)
O(E)
O(logV)
O(V + VlogV + ElogV)
O(V)
는 전체 시간 복잡도에 큰 영향을 미치지 않으므로 삭제,O((V+E)logV)
는 간단하게 O(ElogV)
로 나타낼 수 있음둘다 탐욕 알고리즘이다. (당장 눈앞의 최소 비용을 선택. 결과적으로 최적의 솔루션을 찾음.)
크루스칼 알고리즘은 가장 가중치가 작은 간선부터 선택하면서 최소 신장 트리를 구함
프림 알고리즘은 특정 정점에서 시작하여, 해당 정점에 연결된 가장 가중치가 작은 간선을 선택(간선으로 연결된 정점들에 연결된 간선 중에서 가장 가중치가 작은 간선을 선택)하는 방식으로 최소 신장 트리를 구함
시간 복잡도 비교
Kruskal 알고리즘의 시간 복잡도 : O(N^2)
Prim 알고리즘의 시간 복잡도 : O(ElogE)
(( 어떤 자료구조를 사용하느냐에 따라 시간 복잡도는 달라질 수 있고 개선될 수 있음 ))
정점에 비해 간선이 적은 희소 그래프에서는 Kruskal 알고리즘이 적합
정점에 비해 간선이 많은 밀집 그래프에서는 Prim 알고리즘이 적합