다익스트라를 구현하는 방법은 여러 가지가 있지만 가장 효율적인 방법은 [우선순위 큐]를 활용하는 방법이다.
그래프가 그림과 같이 주어져있다.
A 노드에서 출발한다고 가정한다.
이 때 자기 자신으로 가는 최단 거리는 0이고 나머지 노드들에 대해서는 무한대로 초기화를 한다.
이렇게 최종 최단 거리 테이블이 완성되었다.
import heapq
def dijkstra(graph,start):
distances = {node:float('inf') for node in graph}
distances[start] = 0
queue = []
heapq.heappush(queue,[distances[start],start])
while queue:
current_distance, current_node = heapq.heappop(queue)
if distances[current_node]< current_distance:
continue
for adjacent,weight in graph[current_node].items():
distance = current_distance+weight
if distance < distances[adjacent]:
distances[adjacent] = distance
heapq.heappush(queue,[distance,adjacent])
return distances
문득 이런 생각이 들었다. 왜 우선순위 큐를 사용해야 시간복잡도가 줄어들까? 무슨 연관이 있지??
위 그림에 집중해보자.
A 노드에서 갈 수 있는건 B,C,D 노드
각각 비용이 8,1,2다.
여기서 최소 비용은 1인 C 노드다.
그렇다면 F 노드로 가는 최소 비용을 고려해봤을 때,
B,C,D 중에서 어느 노드를 거쳐 가는 것이 제일 최소 비용일까?
당연히 C 노드이다. 왜냐하면 거리가 제일 짧기 때문.
따라서, 거리 테이블을 업데이트 할 때마다 지속적으로 최소 비용을 찾아주어야 한다.
다시 말해, A 노드에서 거쳐 갈 수 있는 제일 최소 비용인 C를 선택후 거리 테이블을 업데이트 하고
C에서 거쳐갈 수 있는 길(B와 D)중 최소 비용인 D를 선택한다.
같은 방식으로 모든 노드를 거치며 연결 되어 있는 노드들 중 최소 비용인 노드를 선택해가며 거리테이블을 업데이트 해야 하는데
그 과정에서 최소 비용인 노드를 찾는 과정
을 힙으로 구현하면 O(1)에 구현되기 때문에 힙을 쓴다.
만일, 힙을 안 쓰면 연결되어 있는 모든 노드의 개수(최악의 경우 N-1개의 노드를 검사해야함)를 검사해봐야 하기 때문에 O(N)의 시간복잡도가 발생한다.
따라서 힙을 안 쓰면 N개의 노드를 순회하며 그 때마다 N개의 노드 중 최소 비용을 탐색하기 때문에 O(N^2).
힙을 쓰면 N개의 노드를 순회하며 그 때마다 힙에서 팝을 하여 비용을 계산하는데 heappop의 시간복잡도가 O(log N)이기 떄문에 총 O(N*logN)의 시간복잡도가 발생한다.