본문 바로가기

CS Study/algorithm

최단 경로 알고리즘

최단 경로 알고리즘이란?

- 가장 짧은 경로를 찾는 알고리즘

- 지점은 그래프에서 노드로 표현, 지점 간 연결된 도로는 그래프에서 간선으로 표현

 

문제 상황

1) 한 지점에서 다른 한 지점까지의 최단 경로

2) 한 지점에서 다른 모든 지점까지의 최단 경로

3) 모든 지점에서 다른 모든 지점까지의 최단 경로

 

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

- 특정한 노드에서 출발하여 다른 모든 노드로 가는 최단 경로를 계산한다.

- 음의 간선이 없을 때 정상적 동작

- 그리디 알고리즘으로 분류된다. (매 상황에서 가장 비용이 적은 노드를 선택해 임의의 과정을 반복한다.)

- 단계를 거치며 한 번 처리된 노드의 최단 거리는 고정되어 더 이상 바뀌지 않는다.

 

 

다익스트라 알고리즘의 동작 과정

1) 출발 노드를 설정한다.

2) 최단 거리 테이블을 초기화한다.

3) 방문하지 않은 노드 중에서 최단 거리가 가장 짧은 노드를 선택한다.

4) 해당 노드를 거쳐 다른 노드로 가는 비용을 계산하여 최단 거리 테이블을 갱신한다.

5) 3-4번 반복

 

파이썬 코드

 

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[i]
    for i in range(n-1):
        now=get_smallest_node()
        visited[now]=True

        for j in graph[now]:
            cost=distance[now]+j[i]
            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)번에 걸쳐서 최단 거리가 가장 짧은 노드를 매번 선형 탐색해야한다. ➡️ 시간 복잡도는 O(v^2)이다.

노드의 개수가 10,000개를 넘어가는 문제라면 어떻게 해야할까?

 

우선 순위 큐 자료구조를 사용하자.

우선 순위가 가장 높은 데이터를 가장 먼저 삭제하는 자료구조, 대부분의 언어에서 표준 라이브러리 형태로 지원해줌

(큐는 가장 먼저 삽입된 데이터를 꺼내고, 우선순위 큐는 우선순위가 높은 데이터를 꺼낸다.)

 

 

 

➡️ 우선순위 큐를 구현하기위해 힙을 사용한다. (최소 힙, 최대 힙)

힙은 삽입, 삭제 모두 O(log N) 시간 소요

 

import heapq
#파이썬은 minheap으로 구현되어있음.

def heapsort(iterable):
	h=[]
    result=[]
    for value in iterable:
    	heapq.heappush(h,value)
    for i in range(len(h)):
    	result.append(heapq.heappop(h))
    return result
    
result=heapsort([1,3,5,7,9,2,4,6,8,0])
print(result)

 

힙을 이용한 다익스트라 구현

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("갈 수 없어요!")
    else:
        print(distance[i])

힙을 이용하여 다익스트라를 구현하면 시간 복잡도는 O(ElogV)이다.

현재 우선순위 큐에서 꺼낸 노드와 연결된 다른 노드들을 확인하는 총횟수는 최대 간선의 개수(E)만큼 연산이 수행될 수 있다.

직관적으로 전체 과정은 E개의 원소를 우선순위 큐에 넣었다가 빼내는 연산과 매우 유사하다.

 

플로이드 워셜 알고리즘

- 모든 노드에서 다른 노드까지의 최단 경로를 모두 계산한다.

- 단계별로 거쳐 가는 노드를 기준으로 알고리즘을 수행한다. (매 단계마다 방문하지 않은 노드 중에 최단 거리를 갖는 노드를 찾는 과정이 필요하지 않다.)

- 점화식: Dab = min(Dab, Dak+Dkb)

 

ex.

1) 1번 노드를 거쳐가는 경우를 고려하여 테이블 갱신 eg. D23=min(D23, D21+D13)

2) 2번 노드를 거쳐가는 경우를 고려하여 테이블 갱신

 

파이썬 코드

 

성능 분석

노드의 개수가 N개일 때 알고리즘상으로 N번의 단계 수행
- 각 단계마다 O(N^2)의 연산을 통해 현재 노드를 거쳐 가는 모든 경로를 고려한다.

따라서 플로이드 워셜 알고리즘의 총 시간복잡도는 O(N^3)이다.

 

문제) 전보

어떤 나라에는 N개의 도시가 있다. 그리고 각 도시는 보내고자 하는 메시지가 있는 경우, 다른 도시로 전보를 보내서 다른 도시로 해당 메시지를 전송할 수 있다.

하지만 x라는 도시에서 y라는 도시로 전보를 보내고자 한다면, 도시 x에서 y로 향하는 통로가 설치되어 있어야 한다.

예를 들어 x에서 y로 향하는 통로는 있지만, y에서 x로 향하는 통로가 없다면 y는 x로 메시지를 보낼 수 없다.

 

어느날 c라는 도시에서 위급 상황이 발생했다. 그래서 최대한 많은 도시로 메시지를 보내고자 한다. 메시지는 도시 c에서 출발하여 각 도시 사이에 설치된 통로를 거쳐, 최대한 많이 퍼져나갈 것이다.

각 도시의 번호와 통로가 설치되어 있는 정보가 주어졌을 때, 도시 c에서 보낸 메시지를 받게 되는 도시의 개수는 총 몇개이며 도시들이 모두 메시지를 받는데 까지 걸리는 시간은 얼마인지 계산하는 프로그램을 작성하시오.

'CS Study > algorithm' 카테고리의 다른 글

정렬 알고리즘 (문제 풀이)  (0) 2022.07.14
그리디 알고리즘 (문제 풀이)  (0) 2022.07.12
다이나믹 프로그래밍  (0) 2022.07.11
다이나믹 프로그래밍 2 (이론 정리)  (0) 2022.07.08
이진 탐색 알고리즘  (0) 2022.07.07