최단 경로 알고리즘: BFS, 다익스트라, 벨만-포드, 플로이드-워셜, A*
그래프 최단 경로 알고리즘 다섯 가지의 동작 원리와 시간 복잡도, 음수 간선과 음수 사이클에 따른 선택 기준을 정리한다.
시리즈 · 자료구조 · 알고리즘10 / 13
- 시간 복잡도와 공간 복잡도
- 재귀 함수와 꼬리 재귀 최적화
- 연결 리스트(Linked List)
- 스택과 큐: 원형 큐 구현과 덱
- 해시 테이블: 해시 함수와 충돌 처리
- 트리와 이진 탐색 트리
- 힙(Heap)과 우선순위 큐
- 균형 이진 탐색 트리: AVL, Red-Black, B-Tree
- 그래프: 인접 행렬과 인접 리스트
- 최단 경로 알고리즘: BFS, 다익스트라, 벨만-포드, 플로이드-워셜, A*
- 최소 신장 트리(MST): Kruskal, Prim, Union-Find
- 정렬 알고리즘: 퀵·병합·기수 정렬부터 Timsort까지
- Thread-Safe 자료구조: Python GIL과 Java ConcurrentHashMap
한눈에 보기
V는 정점의 수, E는 간선의 수이다.
| 알고리즘 | 구하는 것 | 시간 복잡도 | 음수 간선 |
|---|---|---|---|
| BFS | 한 정점 → 모든 정점 (가중치 없음) | O(V + E) | - |
| 다익스트라 | 한 정점 → 모든 정점 | O(E log V) | 불가 |
| 벨만-포드 | 한 정점 → 모든 정점 | O(VE) | 가능, 음수 사이클 판별 |
| 플로이드-워셜 | 모든 정점 → 모든 정점 | O(V³) | 가능 (음수 사이클은 불가) |
| A* | 한 정점 → 한 정점 | 휴리스틱에 따라 다름 | 불가 |
BFS (Breadth First Search)
시작 노드에서 가까운 노드부터 탐색한다. 거리가 1인 모든 노드를 방문한 뒤 거리가 2인 모든 노드를 방문하는 순서이다.
- 거리 순으로 탐색하므로, 처음 목적지에 도달한 순간이 최단 거리이다.
- 간선마다 비용이 같은(가중치가 없는) 그래프에서만 최단 거리를 보장한다.
- 시간 복잡도: O(V + E). 모든 노드와 간선을 한 번씩 방문하기 때문이다.
다익스트라(Dijkstra)
최단 거리 테이블을 유지하면서, 아직 방문하지 않은 노드 중 비용이 가장 낮은 노드를 선택해 그 노드를 거쳐 가는 경로로 테이블을 갱신하는 알고리즘이다.
- “가장 낮은 비용의 노드”를 빠르게 꺼내기 위해 우선순위 큐(힙)으로 구현한다.
- 시간 복잡도: O(E log V)
음수 가중치가 있으면 쓸 수 없다
다익스트라는 한 번 방문해 확정한 노드의 거리가 이후에 더 줄어들지 않는다고 가정한다. 음수 간선이 있으면 이 가정이 깨져 올바른 결과를 보장하지 못한다.
음수 사이클이 있는 경우는 더 근본적인 문제가 있다. 사이클을 돌수록 비용이 계속 줄어들기 때문에 최단 거리 자체를 정의할 수 없다.

힙을 사용하지 않고 구현한다면
매번 방문하지 않은 노드 전체를 훑어 가장 비용이 낮은 노드를 찾아야 하므로 O(V²)이 된다.
벨만-포드(Bellman-Ford)
다익스트라와 달리 음수 간선이 있어도 최단 거리를 구할 수 있고, 음수 사이클의 존재도 판별할 수 있다. 매 단계에서 모든 간선을 확인하기 때문이다.
- 모든 간선에 대해, 기존에 저장된 거리보다 현재 노드를 거쳐 인접 노드에 도달하는 거리가 더 짧으면 값을 갱신한다.
- 이 과정을 V - 1번 반복한다.
- 마지막으로 한 번 더 수행한다. 이때도 갱신되는 값이 있다면 음수 사이클이 존재하는 것이므로 최단 거리가 없다고 판단한다.
- 시간 복잡도: O(VE). 매 반복에서 모든 간선을 확인하므로 다익스트라보다 느리다.
플로이드-워셜(Floyd-Warshall)
모든 노드에서 모든 노드까지의 최단 거리를 구한다.
- 각 노드를 차례로 중간 노드로 선정하고, 그 노드를 거쳐 가는 경로가 더 짧으면 값을 갱신한다.
- 시간 복잡도: O(V³)
A* 알고리즘
다익스트라는 시작 노드만 지정해 다른 모든 노드까지의 최단 경로를 구한다. A*는 시작 노드와 목적지 노드를 분명하게 지정해 두 노드 사이의 최단 경로를 구한다.
f(n) = g(n) + h(n)
- G: 시작 노드에서 해당 노드까지 실제로 든 비용
- H: 해당 노드에서 목적지까지의 휴리스틱 추정값 (좌표 차이나 직선 거리 등)
- Parent Node: 해당 노드에 도달하기 위해 직전에 거친 노드
h(n) = 0이면 다익스트라와 동일하게 동작한다.
동작 과정
후보 노드를 담는 O(Open List)와 확정된 노드를 담는 C(Close List)를 사용한다.
- 시작 노드를 C에 넣고, 연결된 노드들을 F, G, H, Parent Node와 함께 O에 담는다.
- O에서 F가 가장 작은 노드를 꺼내 C에 추가한다.
- 그 노드와 연결된 노드들을 O에 담는다. 이미 O에 있는 노드는 더 작은 값으로 갱신한다.
- 목적지 노드가 C에 담길 때까지 2~3을 반복한다.

목적지 노드가 C에 담기면, Parent Node를 거꾸로 타고 올라간 경로가 최단 경로가 된다.

성능
성능은 휴리스틱 함수를 어떻게 설정하느냐에 따라 달라진다. 추정이 실제 남은 비용에 가까울수록 불필요한 노드를 덜 탐색한다. 다만 휴리스틱이 실제 비용을 과대평가하면 최단 경로를 보장하지 못한다.
대표적인 휴리스틱 함수로 맨해튼 거리와 유클리드 거리가 있다.
어떤 알고리즘을 써야 할까
- 가중치가 없다 → BFS
- 한 정점에서 다른 모든 정점까지의 최단 거리가 필요하다 → 다익스트라
- 출발 노드와 도착 노드가 정해져 있다 → A*
- 음수 간선이 있거나 음수 사이클 여부를 판별해야 한다 → 벨만-포드
- 모든 정점 쌍의 최단 거리가 필요하다 → 플로이드-워셜
정점이 N개, 간선이 N³개라면?
- BFS: O(N + N³) = O(N³)
- 다익스트라: O(N³ log N)
트리에서 두 노드 사이의 최단 거리는 어떻게 구할까?
트리에서는 두 노드를 잇는 경로가 하나뿐이므로 LCA(최소 공통 조상) 알고리즘을 사용한다. 두 노드의 깊이를 먼저 맞춘 뒤, 동시에 위로 올라가며 처음 만나는 공통 조상을 찾는다. 두 노드에서 공통 조상까지의 거리를 더하면 최단 거리이다.