다익스트라 알고리즘 1 — 각 정점까지의 최단 거리만 구하기

“지금까지 거리를 확정한 정점들 중에서, 가장 가깝게 닿을 수 있는 다음 정점을 하나씩 추가한다.” 다익스트라는 그리디 알고리즘이고, 매 단계 정점을 고르는 방식이 Prim과 많이 닮았다. 차이가 있다면 Prim은 “트리에 가장 가볍게 붙는 간선”를 보고, Dijkstra는 “시작점에서 거리가 가장 짧은 정점”을 본다는 점이다. Prim과의 본격적인 비교는 2편에서 다룬다.

이 포스트에서 다루는 내용
  • Dijkstra의 세 가지 변형 중 1편, 각 정점까지의 최단 거리(길이)만 구하기 (경로 복원과 시간복잡도는 2편에서)
  • 알고리즘의 아이디어: 답을 확정한 정점 집합 RR을 키워가는 그리디 전략
  • 단계별 진행을 작은 그래프에서 시각화
  • 정확성 증명 2가지: “가장 작은 dmind_{min}이 곧 최단거리”와 “새로 추가된 uu의 간선만 갱신해도 충분”

알고리즘의 아이디어

입력은 가중 그래프 G=(V,E,w)G = (V, E, w)와 시작점 v0v_0이고, 정점(vertex) 수는 nn이다. 이 글은 그래프의 점을 정점, 이음선을 간선(edge) 으로 부른다. 여기서 도착점은 따로 정하지 않는다. 임의의 정점 vnv_n까지의 최단 경로를 구하면 그 경로 위에 있는 모든 중간 정점까지의 최단 거리도 자동으로 함께 얻어지기 때문이다. 만약 중간의 어떤 부분 경로가 최단이 아니었다면, 그 부분을 더 짧은 경로로 바꿔서 전체 길이를 줄일 수 있을 텐데, 이는 모순이다. 그래서 처음부터 모든 정점까지의 거리를 한꺼번에 구한다.

각 정점 uu에 대해 현재까지 추정한 최단 거리 dmin(u)d_{min}(u)를 관리한다. 초깃값은 다음과 같다.

  • 간선 (v0,u)(v_0, u)가 존재하면: dmin(u)=w(v0,u)d_{min}(u) = w(v_0, u)
  • 간선이 없으면: dmin(u)=d_{min}(u) = \infty

그리고 이미 답이 확정된 정점들의 집합 RR을 도입한다. 처음에는 R={v0}R = \{v_0\}이다. 시작점까지의 거리는 당연히 00이다.

이제 RR이 모든 정점을 포함할 때까지 다음 세 단계를 반복한다.

  1. RCR^C(아직 확정되지 않은 정점들) 중 dmind_{min}이 가장 작은 정점 uu를 고른다.
  2. uuRR에 추가한다. 이 시점에 dmin(u)d_{min}(u)uu의 진짜 최단거리로 확정되는데, 왜 그런지는 아래 정확성 증명에서 다룬다.
  3. uu에서 나가는 간선을 따라 RCR^C에 있는 인접 정점 vv들의 거리를 dmin(v)=min(dmin(v), dmin(u)+w(u,v))d_{min}(v) = \min(d_{min}(v),\ d_{min}(u) + w(u, v))로 갱신한다.

Pseudo-code

// G = (V, E, w), 시작 정점 v0
R       = { v0 }
d[v0]   = 0
d[v]    =for all v ≠ v0
d[v]    = w(v0, v)             if (v0, v) ∈ E

while (R != V) {
    u = argmin { d[u] : u ∉ R };
    R = R ∪ { u };
    for each (u, v) ∈ E with v ∉ R {
        d[v] = min(d[v], d[u] + w(u, v));
    }
}
return d;

한 번의 반복은 정점 하나를 RR에 추가한다. 시작할 때 R=1|R| = 1이고 끝났을 때 R=n|R| = n이므로, 반복은 정확히 n1n - 1번 일어난다.

이 글은 두 가지 가정 위에서 이야기를 풀어간다. 첫째로 모든 간선 가중치가 00 이상이고, 둘째로 시작점 v0v_0에서 모든 정점에 도달할 수 있다고 본다. 음의 가중치나 도달 불가능한 정점을 처리하는 방법은 후속 글에서 다룬다.


단계별 진행 시각화

작은 그래프 한 개로 알고리즘을 따라가 보자.

  • 정점: A,B,C,D,EA, B, C, D, E (시작점 AA)
  • 간선: AB(2), AC(5), BC(1), BD(7), CD(3), CE(8), DE(2)A{-}B(2),\ A{-}C(5),\ B{-}C(1),\ B{-}D(7),\ C{-}D(3),\ C{-}E(8),\ D{-}E(2)
Dijkstra 진행 4단계 — A에서 시작해 R을 키워가며 거리를 갱신
Dijkstra 진행 4단계 — A에서 시작해 R을 키워가며 거리를 갱신

각 단계에서 선택된 정점은 빨간색으로 채워지고, 그 정점이 갱신한 간선은 굵은 빨간색이 된다. 단계별 변화를 표로 정리하면 다음과 같다.

반복선택된 uuRR (추가 후)갱신된 dmind_{min} 값들
1BB{A,B}\{A, B\}d[C]:53d[C]: 5 \to 3, d[D]:9d[D]: \infty \to 9
2CC{A,B,C}\{A, B, C\}d[D]:96d[D]: 9 \to 6, d[E]:11d[E]: \infty \to 11
3DD{A,B,C,D}\{A, B, C, D\}d[E]:118d[E]: 11 \to 8
4EE{A,B,C,D,E}\{A, B, C, D, E\}

이 그림에서 볼 수 있듯이, Dijkstra는 항상 가까운 정점부터 추가한다. dmind_{min} 값이 작은 순서대로 RR에 들어간다는 뜻이다.1


정확성 증명

Dijkstra가 매 단계 올바른 정점을 RR에 추가한다는 것, 그리고 그때 갱신하는 간선만으로 모든 거리가 정확히 유지된다는 것을 두 명제로 나누어 증명한다.

1. 가장 작은 dmin(u)d_{min}(u)가 정말 uu의 최단거리인가

알고리즘은 RCR^Cdmind_{min}이 가장 작은 정점 uu를 골라 RR에 추가하면서 “dmin(u)d_{min}(u)uu의 최단거리”라고 선언한다. 이게 왜 맞을까?

가정. dmin(u)d_{min}(u)uu의 최단거리가 아니라고 해 보자. 그러면 이보다 더 짧은 어떤 경로 PP가 존재할 것이다. 그런데 현재 dmin(u)d_{min}(u)는 “RR의 정점들만 거쳐서 uu에 도달하는 최단 길이”이므로, PP는 반드시 어딘가에서 RR 바깥으로 빠져나간다. 그렇게 처음으로 RCR^C에 들어가는 정점을 uu'이라고 하자.

증명 1 — R^C를 거치는 우회 경로는 u'를 지나며 더 짧아질 수 없다
증명 1 — R^C를 거치는 우회 경로는 u'를 지나며 더 짧아질 수 없다

관찰. uu' 역시 RCR^C의 원소다. 그러니 uu를 고른 조건(가장 작은 dmind_{min})에 의해

dmin(u)dmin(u)d_{min}(u) \le d_{min}(u')

이 성립한다. 또한 경로 PPuu'에 도달하는 시점의 누적 길이는 이미 dmin(u)d_{min}(u') 이상이다. dmin(u)d_{min}(u')이 “RR만 거쳐 uu'에 도달하는 최단 길이”로 정의되기 때문이다.

결론. 따라서 PP의 전체 길이는

Pdmin(u)dmin(u)|P| \ge d_{min}(u') \ge d_{min}(u)

가 된다. 이는 PPdmin(u)d_{min}(u)보다 짧다는 가정에 모순이다. 그러므로 dmin(u)d_{min}(u)는 곧 uu의 최단거리다.

주장
가정dmin(u)d_{min}(u)uu의 최단거리가 아니다 → 더 짧은 경로 PP 존재
관찰PP는 어느 순간 RCR^C로 빠짐 → 첫 진입점 uRCu' \in R^C 존재
관찰uuRCR^C 중 가장 작은 dmind_{min}을 가지므로 dmin(u)dmin(u)d_{min}(u) \le d_{min}(u')
결론Pdmin(u)dmin(u)\lvert P \rvert \ge d_{min}(u') \ge d_{min}(u) → 모순

2. 왜 새로 추가된 uu의 간선만 갱신해도 충분한가

알고리즘은 uuRR에 추가한 직후, uu에서 직접 나가는 간선만 가지고 RCR^C의 거리를 갱신한다. RR 안에 있는 다른 정점들은 다시 들여다보지 않는다. 이렇게만 해도 왜 충분할까?

의문. uu를 거쳐 다시 RR 안의 어떤 정점 tt를 들렀다가, 거기서 RCR^C의 정점 vv로 빠지는 경로는 왜 따로 고려하지 않아도 될까?

증명 2 — u → t → R^C 우회 경로가 왜 불필요한지
증명 2 — u → t → R^C 우회 경로가 왜 불필요한지

관찰. ttuu보다 먼저 RR에 들어온 정점이다. 이미 RR에 있다는 건 그 시점에 tt의 거리가 확정됐다는 뜻이다. 그때 정해진 dmin(t)d_{min}(t)uu를 거치지 않고 얻은 길이다. 따라서

dmin(t)dmin(u)+w(u,t)u를 거쳐 t로 가는 경로 길이d_{min}(t) \le \underbrace{d_{min}(u) + w(u, t)}_{u \text{를 거쳐 } t \text{로 가는 경로 길이}}

가 성립한다. 만약 이게 성립하지 않는다면 tt의 거리가 더 줄어들 수 있었다는 말이고, 이는 dmin(t)d_{min}(t)가 진짜 최단거리라는 사실과 모순이다.

결론. 그러므로 utvu \to t \to v 경로의 길이는

dmin(u)+w(u,t)+w(t,v)dmin(t)+w(t,v)d_{min}(u) + w(u, t) + w(t, v) \ge d_{min}(t) + w(t, v)

가 된다. 그런데 dmin(t)+w(t,v)d_{min}(t) + w(t, v)ttRR에 들어왔을 때 이미 vv를 갱신하면서 반영된 값이다. 즉 새로운 정보가 아니다. 그래서 uu를 거쳐 tt로 우회하는 경로는 굳이 볼 필요가 없고, uu에서 vv로 직접 가는 간선만 새로 갱신하면 충분하다.

핵심 정리
  • Dijkstra는 답이 확정된 정점 집합 RR을 시작점만 넣고 시작해, 매 단계 RCR^Cdmind_{min}이 가장 작은 정점을 골라 RR에 추가한다.
  • 한 번의 반복이 정점 하나를 확정하므로 정확히 n1n - 1번 반복한 뒤 종료한다.
  • 정확성 1: 가장 작은 dmin(u)d_{min}(u)는 곧 uu의 최단거리다. RCR^C를 한 번이라도 거치는 우회 경로는 dmin(u)d_{min}(u') 이상이고, dmin(u)dmin(u)d_{min}(u) \le d_{min}(u')이므로 더 짧을 수 없기 때문이다.
  • 정확성 2: 새로 추가한 uu에서 직접 나가는 간선만 갱신해도 된다. RR 안의 다른 정점 tt를 우회하는 경로는, tt가 이미 가진 dmin(t)d_{min}(t)가 최단인 이상 새로운 정보가 아니다.
  • 결과적으로 한 시작점에서 모든 정점까지의 최단 거리가 단 한 번의 실행으로 얻어진다.
증명의 묘미

Prim은 cycle 위의 대체 간선 ee'과 비교하는 방식으로, 그리디 선택이 항상 어떤 MST에 포함된다는 것을 보였다. Dijkstra의 증명은 조금 다른 방식이다. “이미 확정된 거리”라는 사실 자체에 기댄다. 새로 고른 uudmin(u)d_{min}(u)를 흔들 수 있는 경로는 반드시 RCR^C를 거쳐야 하는데, RCR^C의 정점은 정의상 모두 dmin(u)d_{min}(u) 이상이기 때문이다. 둘 다 같은 그리디 알고리즘이지만 정확성을 보이는 방법은 이렇게 다르다.

다음 포스트

다익스트라 알고리즘 2 — 최단 경로 복원, 시간 복잡도, 그리고 Prim과의 비교 에서는 거리만이 아니라 최단 경로 자체를 복원하는 방법, 우선순위 큐로 O(mlogn)O(m \log n) 시간을 달성하는 구현, BFS와 Dijkstra의 관계, 그리고 Prim vs Dijkstra의 본격 비교까지 다룬다.

Footnotes

  1. 이 사실의 정식 증명은 후속 글에서 다룬다. 알고리즘이 추가하는 순서대로 dmind_{min}이 단조증가한다는 것을 보이면 된다.

© 2026 XsQuare01. Powered by GitHub Pages. · 방문자