[Algorithm] 최단경로

HyunDong Lee·2021년 9월 21일
0

Algorithm

목록 보기
5/10
post-thumbnail

최단 경로 알고리즘은 말 그대로 가장 짧은 경로를 찾는 알고리즘이다. 그래서 '길찾기' 문제라고도 불린다. 최단 경로 알고리즘 유형에는 다양한 종류가 있는데, 상황에 맞는 효율적인 알고리즘이 이미 정럽되어 있다.

  • 다익스트라
  • 플로이드 워셜
  • 벨만 포드 알고리즘

그리디 + 다이나믹 프로그래밍 알고리즘이 최단 경로 알고리즘에 그대로 적용된다. 위 두 알고리즘의 한 유형으로 볼 수 있다.

다익스트라 최단 경로 알고리즘

→ 다익스트라 최단 경로는 그래프에서 여러 개의 노드가 있을 때, 특정한 노드에서 출발하여 다른 노드로 가는 각가의 최단 경로를 구해주는 알고리즘이다. 다익스트라 최단 경로 알고리즘은 '음의 간선'이 없을 때 정상적으로 동작한다. 음의 간선이란 0보다 작은 값을 가지는 간선을 의미한다. 실제 GPS 소프트웨어의 기본 알고리즘으로 채택된다.

매 번 가장 비용이 적은 노드를 선택하여 임의의 과정을 반복한다.

  1. 출발 노드를 설정한다.
  2. 최단 거리 테이블을 초기화한다.
  3. 방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택한다.
  4. 해당 노드를 거쳐 다른 노드로 가는 비용을 계산하여 최단 거리 테이블을 갱신한다.

'각 노드에 대한 현재까지의 최단 거리'정보를 항상 1차원 리스트에 저장하며 리스트를 계속 갱신한다는 특징이 있다. 매번 현재 처리하고 있는 노드를 기준으로 주변 간선을 확인한다.

다익스트라를 구현하는 방법은 2가지

→ 구현하기 쉽지만 느리게 동작하는 코드

→ 구현하기에 조금 더 까다롭지만 빠르게 동작하는 코드

첫 번째 다익스트라 코드

import sys
#input = sys.stdin.readline
INF = int(1e9)

n, m = map(int, input().split())
start = int(input())
graph = [[] for i in range(n+1)]
visited = [False] * (n+1)
distance = [INF] * (n+1)

for _ in range(m):
    a, b, c = map(int, input().split())
    graph[a].append((b, c))

def get_smallest_node():
    min_value = INF
    index = 0
    for i in range(1, n+1):
        if distance[i] < min_value and not visited[i]:
            min_value = distance[i]
            index = i
    return index
def dijkstra(start):
    distance[start] = 0
    visited[start] = True
    for j in graph[start]:
        distance[j[0]] = j[1]
    for i in range(n-1):
        now = get_smallest_node()
        visited[now] = True
        for j in graph[now]:
            cost = distance[now] + j[1]
            if cost < distance[j[0]]:
                distance[j[0]] = cost
                
dijkstra(start)

for i in range(1, n+1):
    if distance[i] == INF:
        print('INFINITY')
    else:
        print(distance[i])

간단한 다익스트라 알고리즘의 시간 복잡도는 O(V^2)으로 총 V번에 걸쳐서 최단 거리가 가장 짧은 노드를 매번 선형 탐색해야 하고, 현재 노드와 연결된 노드를 매번 일일이 확인하기 때문이다.

⇒ 전체 노드 개수가 5000개 이하라면 일반적으로 이 코드로 문제를 풀 수 있을 것이다, 하지만 노드 개수가 10, 000개를 넘어가는 문제라면 이 코드로는 문제를 해결하기 어렵다.


개선된 다익스트라 알고리즘

  • 개선된 다익스트라의 경우에는 O(ElogV)의 시간 복잡도를 보장한다.
  • 힙 자료구조를 사용하여 특정 노드까지의 최단 거리에 대한 정보를 힙에 담아서 처리하므로 출발 노드로부터 가장 거리가 짧은 노드를 더욱 빠르게 찾을 수 있다. 이 과정에서 선형 시간이 아닌 로그 시간이 걸린다.

힙 설명

우선순위 큐는 우선순위가 가장 높은 데이털르 가장 먼제 삭제하는 것이 특징이다.

heapq가 priorityQueue보다 빠르게 동작한다.

# 개선된 다익스트라 알고리즘
import heapq
import sys
#input = sys.stdin.readline
INF = int(1e9)

n, m = map(int, input().split())
start = int(input())
graph = [[] for i in range(n+1)]
distance = [INF]*(n+1)

for _ in range(m):
    a, b, c = map(int, input().split())
    graph[a].append((b, c))

def dijkstra(start):
    q = []
    heapq.heappush(q, (0, start))
    distance[start] = 0
    while q:
        dist, now = heapq.heappop(q)
        if distance[now] < dist:
            continue
        for i in graph[now]:
            cost = dist + i[1]
            if cost < distance[i[0]]:
                distance[i[0]] = cost
                heapq.heappush(q, (cost, i[0]))
dijkstra(start)

for i in range(1, n+1):
    if distance[i] == INF:
        print("INFINITY")
    else:
        print(distance[i])

플로이드 워셜 알고리즘

# 플로이드 워셜 알고리즘
INF = int(1e9)

n = int(input())
m = int(input())
graph = [[INF]*(n+1) for _ in range(n+1)]
for a in range(1, n+1):
    for b in range(1, n+1):
        if a == b:
            graph[a][b] = 0

for _ in range(m):
    a, b, c = map(int, input().split())
    graph[a][b] = c

for k in range(1, n+1):
    for a in range(1, n+1):
        for b in range(1, n+1):
            graph[a][b] = min(graph[a][b], graph[a][k] + graph[k][b])
for a in range(1, n+1):
    for b in range(1, n+1):
        if graph[a][b] ==INF:
            print("INFINITY", end = ' ')
        else:
            print(graph[a][b], end = ' ')
    print()

0개의 댓글