본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Algorithm

최단 경로 알고리즘 고르기

작성 2026. 10. 30.추가
목차9개 항목
출처와 참고

fru1tworld/cs-basic 모의 면접 질문 목록의 「자료구조」 그래프 질문이 묻는 개념을 바탕으로 정리한 노트다.

최단 경로 문제의 갈래

최단 경로(shortest path)는 간선 가중치의 합이 가장 작은 경로다. 알고리즘은 문제의 네 가지 조건으로 갈린다.

  1. 가중치가 없는가(모두 같은가), 0과 1뿐인가, 음이 아닌가, 음수도 있는가
  2. 음수 사이클이 있을 수 있는가
  3. 출발점 하나에서 모든 정점까지(단일 출발점)인가, 모든 정점 쌍(전체 쌍)인가
  4. 목적지가 하나로 정해져 있고 목적지까지의 거리를 어림할 수 있는가

무엇을 고르나

조건알고리즘시간핵심
가중치 없음BFSO(V + E)큐에서 먼저 꺼낸 정점이 가장 가깝다
가중치가 0 또는 10-1 BFSO(V + E)덱을 써서 0 간선은 앞에, 1 간선은 뒤에 넣는다
음수 없음, 단일 출발점다익스트라힙 O(E log V), 배열 O(V²)가장 가까운 미확정 정점을 확정하는 그리디
음수 간선 있음, 단일 출발점벨만-포드O(VE)모든 간선 완화를 V-1번 반복. 음수 사이클을 찾아낸다
DAG(사이클 없음), 음수 가능위상 순서 완화O(V + E)위상 정렬 순서로 한 번씩만 완화하면 된다
전체 쌍, V가 수백 이하플로이드-워셜O(V³)거쳐 갈 정점을 하나씩 늘리는 DP. 음수 간선 가능
전체 쌍, 희소 그래프, 음수 간선존슨(Johnson)O(VE log V)벨만-포드로 가중치를 음이 아니게 바꾼 뒤 다익스트라를 V번
목적지 하나, 거리를 어림하는 함수가 있음A*최악은 다익스트라와 같다「지금까지 거리 + 남은 거리 추정」이 작은 정점부터
트리DFS·BFS 한 번O(V)두 정점 사이 경로가 하나뿐이다

모든 알고리즘의 기본 동작은 완화(relaxation) 다. 간선 u→v(가중치 w)에 대해 dist[u] + w < dist[v]이면 dist[v]를 줄인다. 알고리즘의 차이는 완화를 어떤 순서로, 몇 번 하느냐다.

벨만-포드: 음수 간선

다익스트라는 한번 확정한 정점이 다시 줄지 않는다는 가정에 기대는데, 음수 간선이 있으면 나중에 돌아오는 경로가 더 짧을 수 있어 이 가정이 깨진다. 벨만-포드(Bellman-Ford)는 확정을 포기하고 모든 간선을 V-1번 완화한다.

  • 음수 사이클이 없다면 최단 경로는 같은 정점을 두 번 지나지 않으므로 간선이 많아야 V-1개다. i번째 반복이 끝나면 간선 i개 이하로 가는 최단 거리가 모두 맞아 있으므로 V-1번이면 충분하다.
  • V번째 반복에서도 줄어드는 거리가 있으면 출발점에서 닿는 음수 사이클이 있다. 그 사이클을 돌 때마다 거리가 줄어 최단 거리가 정의되지 않는다(-∞).
def bellman_ford(n, edges, start):          # edges: (u, v, w)
    INF = float('inf')
    dist = [INF] * n
    dist[start] = 0
    for _ in range(n - 1):
        updated = False
        for u, v, w in edges:
            if dist[u] != INF and dist[u] + w < dist[v]:
                dist[v] = dist[u] + w
                updated = True
        if not updated:                      # 더 줄지 않으면 일찍 끝낸다
            break
    for u, v, w in edges:                    # 한 번 더 줄면 음수 사이클
        if dist[u] != INF and dist[u] + w < dist[v]:
            return None
    return dist

dist[u] != INF 확인을 빼면 닿지 않는 정점끼리의 음수 간선 때문에 무한대가 줄어드는 잘못된 갱신이 생긴다. 거리가 바뀐 정점의 이웃만 큐로 다시 보는 변형이 SPFA로, 평균은 빠르지만 최악은 O(VE) 그대로다.

플로이드-워셜: 모든 쌍

플로이드-워셜(Floyd-Warshall)은 「1~k번 정점만 거쳐 갈 수 있을 때 i에서 j까지 최단 거리」를 k를 늘려 가며 구하는 동적 계획법이다.

d[i][j] = min(d[i][j], d[i][k] + d[k][j])    k = 1, 2, …, V 순서로
def floyd_warshall(d):                       # d: V×V 인접 행렬, 간선 없으면 INF, d[i][i] = 0
    n = len(d)
    for k in range(n):                       # 거쳐 갈 정점이 가장 바깥 반복이어야 한다
        for i in range(n):
            for j in range(n):
                if d[i][k] + d[k][j] < d[i][j]:
                    d[i][j] = d[i][k] + d[k][j]
    return d
  • 시간 O(V³), 공간 O(V²). V가 500이면 1억 2500만 번이라 코딩 테스트에서는 V가 수백 이하일 때 쓴다.
  • k를 안쪽 반복에 두면 아직 갱신되지 않은 값을 써서 틀린다. 점화식의 단계가 k이기 때문이다.
  • 음수 간선이 있어도 되고, 끝난 뒤 d[i][i] < 0인 i가 있으면 음수 사이클이 있다.

음수 간선과 음수 사이클

상황쓸 수 있는 것쓸 수 없는 것
음수 간선, 음수 사이클 없음벨만-포드(SPFA), 플로이드-워셜, DAG면 위상 순서 완화, 전체 쌍이면 존슨다익스트라. 이미 확정한 거리가 나중에 줄 수 있다
음수 사이클 있음벨만-포드·플로이드-워셜로 존재를 판정한다. 사이클에서 닿는 정점의 거리는 -∞로 표시한다최단 거리 자체가 정의되지 않는다. 「같은 정점을 두 번 지나지 않는 경로」로 바꾸면 NP-난해 문제가 된다

무방향 그래프의 음수 간선은 그 자체로 음수 사이클이다. 양쪽으로 오가면 계속 줄어든다.

A*: 목적지가 하나일 때

A*(에이 스타)는 다익스트라가 힙에서 g(n)(출발점에서 n까지의 거리)이 가장 작은 정점을 꺼내는 대신, f(n) = g(n) + h(n) 이 가장 작은 정점을 꺼낸다. h(n)은 n에서 목적지까지 남은 거리의 추정치(휴리스틱)다. 목적지 방향의 정점을 먼저 보게 되어 다익스트라처럼 사방으로 퍼지지 않는다.

휴리스틱 성질조건결과
허용 가능(admissible)h(n)이 실제 남은 거리를 넘지 않는다찾은 경로가 최단임이 보장된다
일관(consistent)모든 간선 u→v에서 h(u) ≤ w(u, v) + h(v)정점을 한 번 꺼내면 다시 열 필요가 없다. 다익스트라처럼 동작한다
h = 0-다익스트라와 같다
실제보다 큰 추정허용 불가더 빨리 끝나지만 최단을 놓칠 수 있다

격자 지도에서는 상하좌우 이동이면 맨해튼 거리 |dx| + |dy|, 대각선까지 같은 비용이면 체비쇼프 거리 max(|dx|, |dy|), 아무 방향이나 갈 수 있으면 유클리드 거리를 쓴다.

다익스트라와 비교한 성능은 휴리스틱의 질에 달렸다. 실제 거리에 가까울수록 꺼내는 정점 수가 줄어 목적지 하나를 찾는 데는 크게 빠르다. 휴리스틱이 쓸모없으면(h = 0) 다익스트라와 같고, 최악의 시간 복잡도도 다익스트라와 같다. 다만 출발점에서 모든 정점까지의 거리가 필요하면 A*의 이점이 없다. 열린 정점을 모두 힙에 들고 있어 메모리를 많이 쓴다는 것이 약점이다. 게임의 길 찾기, 로봇 경로 계획, 퍼즐 상태 공간 탐색에 쓴다.

트리에서의 최단 거리

트리는 두 정점 사이 경로가 정확히 하나라 「가장 짧은 경로」를 고를 일이 없다. 출발점에서 DFS나 BFS를 한 번 돌며 dist[child] = dist[parent] + w로 누적하면 모든 정점까지의 거리가 O(V)에 나온다. 가중치가 음수여도 같다. 다익스트라의 힙이 필요 없다.

여러 쌍의 거리를 물으면 루트에서 각 정점까지의 거리 root_dist를 한 번 구해 두고, dist(u, v) = root_dist[u] + root_dist[v] - 2 × root_dist[LCA(u, v)]로 답한다(최소 공통 조상). 트리의 지름(가장 먼 두 정점)은 아무 정점에서 가장 먼 정점 a를 찾고, a에서 다시 가장 먼 정점까지의 거리로 구한다(음이 아닌 가중치일 때).

밀집 그래프: 간선이 V²에 가깝거나 더 많을 때

정점 N개, 간선 N³개처럼 간선이 많으면 복잡도의 E 항이 지배한다.

알고리즘E = N³을 그대로 넣으면중복 간선을 접어 E ≤ N² 이면
다익스트라(이진 힙)O(N³ log N)O(N² log N)
다익스트라(배열, 인접 행렬)O(N²)O(N²)
벨만-포드O(N⁴)O(N³)
플로이드-워셜(전체 쌍)O(N³)O(N³)

단일 출발점이면 인접 행렬 + 배열 다익스트라 O(V²)가 가장 낫다. 간선을 하나씩 보지 않고 정점마다 행렬의 한 행을 훑기 때문에 간선 수와 무관하다. 단순 그래프는 간선이 V²을 넘을 수 없으므로 간선이 N³개라면 중복 간선이고, 입력을 읽으며 쌍마다 최솟값만 남긴다(그래프의 기본과 표현 방법). 전체 쌍이 필요하면 플로이드-워셜 O(N³)이 다익스트라를 N번 돌리는 O(N³)과 같으면서 구현이 짧다.

정리

  • 최단 경로 알고리즘은 가중치 종류, 음수 사이클, 단일 출발점·전체 쌍, 목적지 하나 여부로 고른다.
  • 모든 알고리즘은 완화의 순서와 횟수만 다르다. 다익스트라는 가까운 순서로 한 번, 벨만-포드는 전체를 V-1번, DAG는 위상 순서로 한 번.
  • 음수 간선이면 벨만-포드나 플로이드-워셜. 둘 다 음수 사이클을 판정한다(V번째 완화가 일어남, d[i][i] < 0).
  • A*는 허용 가능한 휴리스틱으로 목적지 쪽부터 탐색해 같은 최단 경로를 더 적은 탐색으로 찾는다.
  • 트리는 경로가 하나라 DFS 한 번이면 되고, 밀집 그래프의 단일 출발점은 O(V²) 다익스트라가 낫다.

관련 노트: 다익스트라 최단 경로, BFS, 그래프의 기본과 표현 방법, 위상 정렬, 동적 계획법, 최소 공통 조상 (LCA)

원본 기록1

댓글

GitHub 계정으로 댓글을 남길 수 있어요.