다익스트라 알고리즘 (Dijkstra's Algorithm)
정의
다익스트라 알고리즘 (Dijkstra’s Algorithm) 은 음이 아닌 가중치 그래프에서 단일 시작점 s 로부터 모든 정점까지의 최단 거리를 찾는 그리디 알고리즘. Edsger W. Dijkstra 가 1956년 고안, 1959년 발표. 우선순위 큐를 쓸 경우 O((V + E) log V).
문제 상황과 동기
그래프 G(V, E) 에서 시작점 s 로부터 모든 정점까지의 최단 거리를 구하고 싶다.
- naive (BFS): 모든 간선 가중치가 1일 때만 O(V + E). 가중치가 다르면 틀림.
- Bellman-Ford: 음수 가중치 허용, O(VE). 음수가 없는 경우 너무 느림.
- Dijkstra: 음이 아닌 가중치 전용, O((V + E) log V). PS 최단 경로의 사실상 표준.
핵심 통찰: 이미 확정된 정점에서 출발한 간선은 다시 안 본다. 거리 작은 순서대로 확정하면, 나중에 더 짧은 경로가 나타날 수 없다 (음수 간선 없으므로).
시각화
핵심 아이디어
invariant: dist[v] = s 에서 v 로의 최단 거리 (확정 or 후보 중 최소). 확정되지 않은 정점 중 dist 가 가장 작은 u 를 확정하고, u 의 이웃을 완화 (relax).
relax(u, v, w):
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
parent[v] = u
우선순위 큐로 “확정 안 된 정점 중 dist 최소” 를 O(log V) 에 추출.
간선이 음수가 아니므로, 한번 확정된 정점은 나중에 더 짧아질 수 없다. 이것이 Bellman-Ford 와의 차이.
알고리즘
Dijkstra(G, s):
dist[s] = 0, dist[v!=s] = ∞
pq = priority_queue (min-heap by dist)
pq.push((0, s))
while pq not empty:
(d, u) = pq.pop()
if d > dist[u]: continue # 이미 확정된 거리보다 큰 old entry
for (v, w) in neighbors(u):
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
pq.push((dist[v], v))
구현
// O((V+E) log V) 우선순위 큐 Dijkstra
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii; // (dist, node)
int main() {
int n, m, s;
cin >> n >> m >> s; // n 정점, m 간선, s 시작점 (1-indexed)
vector<vector<pii>> adj(n + 1);
for (int i = 0; i < m; i++) {
int u, v, w;
cin >> u >> v >> w;
adj[u].push_back({v, w});
}
vector<int> dist(n + 1, 1e9);
priority_queue<pii, vector<pii>, greater<pii>> pq;
dist[s] = 0;
pq.push({0, s});
while (!pq.empty()) {
auto [d, u] = pq.top(); pq.pop();
if (d > dist[u]) continue; // old entry
for (auto [v, w] : adj[u]) {
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}
}
for (int i = 1; i <= n; i++)
cout << (dist[i] == 1e9 ? -1 : dist[i]) << " ";
}5 6 1
1 2 2
1 3 3
2 3 1
2 4 5
3 4 2
4 5 10 2 3 5 6복잡도
| 항목 | 값 |
|---|---|
| 시간 (우선순위 큐) | O((V + E) log V) |
| 시간 (배열 scan) | O(V^2 + E) - 밀집 그래프만 |
| 공간 | O(V + E) |
| 음수 간선 | ✗ (Bellman-Ford 필요) |
증명 스케치
귀류법: 정점 u 를 확정할 때, 나중에 더 짧은 경로 s -> … -> u’ (확정) -> … -> u 가 나타난다 가정. 그럼 u’ 확정 시점에 dist[u’] < dist[u]. 음이 아닌 가중치이므로 u’ -> … -> u 경로는 dist[u’] 이상, 따라서 dist[u] 보다 짧을 수 없다. 모순.
변형 / 활용
| 변형 | 설명 |
|---|---|
| 역방향 간선 | 그래프를 역으로 뒤집어 “모든 정점 -> t” 구하기 |
| 경로 복원 | parent 배열로 역추적 |
| K번째 최단 경로 | 우선순위 큐에 (dist, node, k_count) 튜플 |
| Lazy deletion | dist 갱신 후 old entry 를 큐에서 제거 안 함. 꺼낼 때 skip |
| 다중 소스 | 가상 super source s 를 추가해 모든 시작점과 연결 |
| Dial’s algorithm | 가중치가 작은 정수일 때 배열 버킷 O(V + E·W) |
| A* search | heuristic h(v) 추가, dist[u] + h(u) 로 우선순위 |
함정
1. 음수 간선
간선이 하나라도 음수면 Dijkstra 는 틀림. 벨만-포드 사용.
2. old entry skip 안 함
우선순위 큐에서 꺼낸 (d, u) 의 d 가 dist[u] 보다 크면 이미 더 짧은 경로로 확정된 것. 무시 안 하면 O(V^2) 로 폭발.
3. 1e9 vs LLONG_MAX
초기값 INF 를 LLONG_MAX 로 하면 dist[u] + w 오버플로우. 1e9 또는 1e18 정도 적당.
4. 무방향 그래프
양방향 간선은 adj[u].push_back({v, w}); adj[v].push_back({u, w}); 둘 다.
5. parent 배열
경로 복원 원할 때 완화 시점에 parent[v] = u 기록. 역추적으로 경로 출력.
BOJ 연습 문제
| 번호 | 제목 | 정답률 | 링크 |
|---|---|---|---|
| BOJ 1753 | 최단경로 | 28.7% | kokoa-lab |
| BOJ 1916 | 최소비용 구하기 | 38.2% | kokoa-lab |
| BOJ 4485 | 녹색 옷 입은 애가 젤다지? | 46.8% | kokoa-lab |
| BOJ 1504 | 특정한 최단 경로 | 30.5% | kokoa-lab |
| BOJ 9370 | 미확인 도착지 | 30.1% | kokoa-lab |
참고
이 글의 용어 (4개)
- 너비 우선 탐색 (BFS)algorithm
- 정의 너비 우선 탐색 (Breadth-First Search, BFS) 는 그래프 G=(V, E) 에서 시작 정점 s 로부터 가까운 정점부터 순서대로 방문하는 알고리즘. 큐 (F…
- 벨만-포드 알고리즘 (Bellman-Ford Algorithm)algorithm
- 정의 벨만-포드 알고리즘 (Bellman-Ford Algorithm) 은 음수 가중치 간선을 허용하면서 단일 시작점 s 로부터 모든 정점까지의 최단 거리를 찾는 DP 기반 알고리…
- 우선순위 큐 (Priority Queue)algorithm
- 정의 우선순위 큐 (Priority Queue) 는 원소 중 최댓값 (또는 최솟값) 을 O(log N) 에 추출하는 자료구조. 내부 구현은 대개 힙 (heap). - Max-He…
- 플로이드-워셜 알고리즘 (Floyd-Warshall Algorithm)algorithm
- 정의 플로이드-워셜 알고리즘 (Floyd-Warshall Algorithm) 은 모든 정점 쌍 (i, j) 사이의 최단 거리를 구하는 DP 알고리즘. Robert W. Floyd…
이 개념을 다룬 위키 페이지 (14)
- wiki우선순위 큐 (Priority Queue)
- wikiPriority Queue / Heap: 우선순위 큐
- wiki그리디 (Greedy)
- wiki0-1 BFS
- wiki벨만-포드 알고리즘 (Bellman-Ford Algorithm)
- wiki너비 우선 탐색 (BFS)
- wiki플로이드-워셜 알고리즘 (Floyd-Warshall Algorithm)
- wiki그래프 순회 (Graph Traversal)
- wiki격자 그래프 (Grid Graph)
- wiki최단 경로 (Shortest Path)
- wikiJohnson's Algorithm: sparse APSP
- wikiA* (A-star) 알고리즘
- wiki양방향 탐색 (Bidirectional Search)
- wikiDial's Algorithm (버킷 기반 다익스트라)
💬 댓글