다익스트라
다익스트라(Dijkstra) 알고리즘은 가중치가 있는 그래프에서 시작 노드로부터 모든 노드까지의 최단 거리를 구합니다. 핵심은 "아직 확정되지 않은 노드 중 현재까지 알려진 거리가 가장 짧은 노드를 하나씩 확정해나간다"는 것입니다.
핵심 개념
거리 추정값 (dist)
시작 노드는 거리 0, 나머지는 모두 ∞(무한대)로 시작합니다. 알고리즘이 진행되며 더 짧은 경로를 찾을 때마다 이 추정값이 갱신됩니다.
핵심 개념
확정(finalize)
아직 확정 안 된 노드 중 거리 추정값이 가장 작은 노드를 골라 "이 거리가 최종 확정"이라고 못박습니다. 음수 가중치가 없다면 한 번 확정된 노드의 거리는 이후 바뀌지 않습니다.
핵심 개념
완화(relaxation)
노드를 확정할 때마다 그 노드와 연결된 인접 노드들의 거리를 "확정된 노드까지 거리 + 간선 가중치"와 비교해, 더 짧으면 갱신합니다.
핵심 개념
한 노드가 여러 번 갱신될 수 있다
아직 확정되지 않은 노드의 거리 추정값은 더 짧은 경로가 발견될 때마다 계속 갱신됩니다. 이 시각화의 예제에서는 한 노드가 세 번이나 갱신됩니다.
핵심 개념
왜 음수 가중치는 안 되는가
다익스트라는 "한 번 확정하면 그 거리가 최종"이라고 가정합니다. 간선에 음수 가중치가 있으면 나중에 더 짧은 경로가 나타나 이 가정이 깨질 수 있어, 다익스트라로는 올바른 결과를 보장할 수 없습니다.
핵심 개념
최단 경로 트리
각 노드를 확정할 때 "어느 노드를 거쳐 왔는지(prev)"를 함께 기록해두면, 알고리즘이 끝난 뒤 시작 노드에서 임의의 노드까지의 최단 경로를 역추적할 수 있습니다.
다익스트라가 풀려던 문제
다익스트라 알고리즘은 에츠허르 다익스트라(Edsger W. Dijkstra)가 1959년 Numerische Mathematik에 실은 짧은 논문 "A note on two problems in connexion with graphs"에서 소개한 것으로 알려져 있습니다. 가중치가 있는 그래프에서 한 시작점으로부터 다른 모든 노드까지의 최단 거리를 구합니다. 오늘날에는 네트워크 라우팅 프로토콜의 경로 계산에도 쓰입니다.
숫자로 확인하기
자주 하는 오해
면접에서 이렇게 물어요
면접 질문
음수 가중치가 있으면 왜 다익스트라가 안 되나요?
다익스트라는 확정한 노드의 거리가 이후에 바뀌지 않는다고 가정합니다. 음수 간선이 있으면 이미 확정한 노드로 더 짧게 가는 길이 나중에 생길 수 있어서 이 가정이 깨집니다.
면접 질문
우선순위 큐를 쓰면 왜 빨라지나요?
매 단계에서 확정 안 된 노드 중 거리가 가장 작은 것을 전체 탐색(O(V))으로 찾는 대신 힙에서 O(log V)에 꺼냅니다. 이진 힙을 쓰면 전체가 O((V + E) log V)입니다.
면접 질문
BFS와 다익스트라는 어떻게 다른가요?
모든 간선의 가중치가 같다면 BFS가 최단 거리를 구합니다. 가중치가 제각각이면 간선 수가 적은 경로가 짧은 경로가 아닐 수 있어서 다익스트라가 필요합니다.
면접 질문
최단 경로 자체는 어떻게 복원하나요?
노드를 갱신할 때 어느 노드를 거쳐 왔는지(이전 노드)를 같이 저장합니다. 끝난 뒤 목적지에서 이전 노드를 따라 시작점까지 거슬러 올라간 뒤 뒤집습니다.
실제 시스템에서는
PriorityQueue는 힙 기반 우선순위 큐로 넣기와 꺼내기가 O(log n)입니다. 이미 들어 있는 항목의 우선순위를 낮추는 연산은 제공하지 않아서, 더 짧은 거리가 나오면 새 항목을 추가하고 뒤늦게 나오는 오래된 항목은 건너뛰는 방식으로 구현하는 것이 일반적입니다.
heapq가 최소 힙을 제공합니다.
함께 보면 좋은 토픽
참고 자료
Java — 다익스트라 (배열 기반, O(V²))
/* ── 매 단계 미확정 노드 중 거리가 가장 짧은 노드를 선택 ───── */ public int[] dijkstra(int src, int[][] adjMatrix) { int n = adjMatrix.length; int[] dist = new int[n]; boolean[] visited = new boolean[n]; Arrays.fill(dist, Integer.MAX_VALUE); dist[src] = 0; for (int count = 0; count < n; count++) { int u = -1; for (int i = 0; i < n; i++) { // 미확정 노드 중 최솟값 탐색 — O(V) if (!visited[i] && (u == -1 || dist[i] < dist[u])) u = i; } if (dist[u] == Integer.MAX_VALUE) break; // 남은 노드가 모두 도달 불가능 visited[u] = true; for (int v = 0; v < n; v++) { // u의 모든 인접 노드 완화(relax) if (adjMatrix[u][v] > 0 && !visited[v] && dist[u] + adjMatrix[u][v] < dist[v]) { dist[v] = dist[u] + adjMatrix[u][v]; } } } return dist; }
바깥 반복문이 n번, 매번 최솟값 탐색에 O(V) + 완화에 O(V)가 걸려 전체 O(V²)입니다. 노드 수가 적을 때는 단순하고 충분히 빠릅니다.
dist[u] + adjMatrix[u][v] < dist[v] 비교가 바로 "완화(relaxation)" — 확정된 노드를 거쳐 가는 것이 더 짧다면 갱신합니다.
Java — 다익스트라 (우선순위 큐, O((V+E) log V))
/* ── 최소 힙으로 "다음 확정 후보"를 빠르게 꺼냄 ───────────── */ public int[] dijkstraPQ(int src, List<List<int[]>> adj, int n) { int[] dist = new int[n]; Arrays.fill(dist, Integer.MAX_VALUE); dist[src] = 0; PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]); // [노드, 거리] 오름차순 pq.offer(new int[]{src, 0}); while (!pq.isEmpty()) { int[] cur = pq.poll(); int u = cur[0], d = cur[1]; if (d > dist[u]) continue; // 이미 더 짧은 경로로 확정된 노드 — 오래된 항목 무시 for (int[] edge : adj.get(u)) { int v = edge[0], w = edge[1]; if (d + w < dist[v]) { dist[v] = d + w; pq.offer(new int[]{v, dist[v]}); // 갱신된 거리로 큐에 다시 추가 } } } return dist; }
배열 버전과 원리는 같지만, "미확정 노드 중 최솟값 찾기"를 매번 O(V)로 훑는 대신 우선순위 큐가 O(log V)에 꺼내주므로 훨씬 빠릅니다.
같은 노드가 큐에 중복으로 들어갈 수 있어 if (d > dist[u]) continue;로 이미 쓸모없어진(더 나은 경로로 갱신된) 오래된 항목을 걸러냅니다.
Java — 최단 경로 역추적 (prev 배열)
/* ── 완화 성공 시 prev[v]=u를 기록해두면 경로 복원 가능 ────── */ if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; prev[v] = u; // "v는 u를 거쳐서 왔다"를 기록 } /* ── 목표 노드에서 시작 노드 방향으로 거슬러 올라가며 경로 복원 ── */ public List<Integer> reconstructPath(int target, int[] prev) { List<Integer> path = new LinkedList<>(); for (int at = target; at != -1; at = prev[at]) { path.add(0, at); // 맨 앞에 삽입 → 최종적으로 시작→목표 순서가 됨 } return path; }
거리(dist)만 구하면 "얼마나 가까운지"는 알 수 있지만 "어떤 경로로 가는지"는 알 수 없습니다. prev 배열이 그 경로 정보를 담습니다.
목표 노드에서 시작 노드까지 prev를 따라 거슬러 올라간 뒤 순서를 뒤집으면(또는 맨 앞에 삽입하면), 시작 노드부터 목표 노드까지의 최단 경로가 만들어집니다.
Q1
다익스트라 알고리즘이 다루는 문제는?
Q2
다익스트라 알고리즘에서 '확정'된 노드의 의미는?
Q3
완화(Relaxation) 연산이 하는 일은?