-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* docs: add dijkstra algorithm based on priority queue * docs: add dijkstra example code
- Loading branch information
Showing
1 changed file
with
253 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,253 @@ | ||
--- | ||
title: Priroity queue를 이용한 Dijkstra | ||
author: jinwoo | ||
date: 2025-02-16 17:00:00 +0900 | ||
categories: [Software engineering, Algorithm] | ||
tags: [Algorithm, STL, C++] | ||
render_with_liquid: false | ||
mermaid: true | ||
description: Priroity queue를 이용한 Dijkstra 알고리즘 구현 | ||
--- | ||
|
||
## Dijkstra alogrithm | ||
|
||
### 개요 | ||
|
||
- 가중 그래프에서 한 정점에서 다른 모든 정점까지의 최단 경로를 찾는 알고리즘 | ||
- 음수 가중치를 허용하지 않음 | ||
- 방문하지 않은 정점 중 가장 거리가 짧은 정점을 선택하여 인접한 정점들의 거리를 갱신 | ||
|
||
### 동작 원리 | ||
|
||
1. 시작 정점을 최단 거리 0 으로 설정하고, 나머지 정점과의 최단 거리를 INF(무한대)로 설정 | ||
2. 현재 방문할 정점을 선택 | ||
3. 해당 정점과 인접한 정점들의 거리를 갱신 | ||
4. 모든 정점에 대해 위 과정을 반복 | ||
|
||
### 핵심 연산 | ||
|
||
- 최소 거리 정점 선택 → 현재 방문하지 않은 정점 중 가장 최단 거리가 작은 정점을 선택 | ||
- 거리 갱신 → 선택된 정점을 기준으로 인접한 정점들의 거리를 업데이트 | ||
|
||
### 자료구조와 연산 | ||
|
||
- `array`, `list` 등을 사용하여 찾으면 **O(V)**의 시간이 소요 | ||
- `priority_queue`를 활활용하면 **O(log V)**로 줄일 수 있음 | ||
|
||
## 시간 복잡도 | ||
|
||
정점(vertex)의 수 `V`, 간선(edge)의 수 `E`로 할 때 각 자료구조 별 시간 복잡도는 아래와 같음 | ||
|
||
### 시간 복잡도 (배열) | ||
|
||
배열을 사용할 경우, 총 시간복잡도가 `O(V²)`가 되어 그래프가 dense할 수록 비효율성이 높아짐 | ||
|
||
1. 최소 거리 정점 선택 | ||
- 최단 거리 노드를 찾는 작업에 모든 정점 탐색 필요 → `O(V)` | ||
- 시간 복잡도: 모든 vertex에 대하여 계산 → `O(V²)` | ||
2. 거리 갱신 | ||
- 선택된 정점에서 인접한 간선들을 모두 확인 | ||
- 시간 복잡도: 모든 edge에 대하여 계산 → `O(E)` | ||
3. 전체 시간 복잡도 | ||
- `O(V² + E)` ≈ `O(V²)` | ||
|
||
### 시간 복잡도 (우선순위 큐) | ||
|
||
priority_queue(최소 힙)를 사용하면 오름차순(작은 값 → 큰 값)으로 정렬 | ||
|
||
1. 최소 거리 정점 선택 | ||
- `top()` 으로 최소 거리를 가진 정점을 `O(1)` 에 가져올 수 있음 | ||
- `pop()` 의 시간 복잡도는 `O(log V)` 이므로 빠르게 삭제 가능 | ||
- 시간 복잡도: priority queue에 삽입되는 원소의 수는 edge에 비례할 수 있음 → `O(E log V)` | ||
2. 거리 갱신 | ||
- 거리 값이 갱신되면 `push()` 를 이용하여 새로운 값 삽입 → `O(log V)` | ||
- 시간 복잡도: 모든 edge에 대하여 발생할 경우 `O(E log V)` | ||
3. 전체 시간 복잡도 | ||
- `O(E log V)` + `O(E log V)` = `O(E log V)` | ||
|
||
## 알고리즘 구현 | ||
|
||
예제 코드는 [grade-e/dijkstra-pq-container](https://github.com/grade-e/dijkstra-pq-container/tree/main) | ||
를 참고 | ||
|
||
### 소스 코드 | ||
|
||
동작 원리는 주석 참고 | ||
|
||
```c++ | ||
#include <iostream> | ||
#include <limits> | ||
#include <queue> | ||
#include <utility> | ||
#include <vector> | ||
|
||
using namespace std; | ||
constexpr int INF = numeric_limits<int>::max(); | ||
|
||
struct Node { | ||
int vertex; | ||
int cost; | ||
bool operator>(const Node& other) const { // for min-heap | ||
return this->cost > other.cost; | ||
} | ||
}; | ||
|
||
vector<int> dijkstra(int start, int V, vector<vector<Node>>& graph) { | ||
// pq를 사용하여 (거리, 정점) 쌍을 관리 | ||
// min heap pq(오름차순)를 설정 | ||
priority_queue<Node, vector<Node>, greater<Node>> pq; | ||
|
||
// 거리 테이블 dist를 무한으로 초기화 | ||
vector<int> dist(V, INF); | ||
|
||
// 초기 위치 거리 값을 0으로 설정, pq에 삽입 | ||
dist[start] = 0; | ||
pq.push({start, 0}); | ||
while (!pq.empty()) { | ||
// priority_queue에서 최소 비용인 node를 꺼냄 | ||
Node current = pq.top(); | ||
pq.pop(); | ||
|
||
// 현재 node의 cost가 표 값보다 높으면 skip | ||
int u = current.vertex; | ||
int currentCost = current.cost; | ||
if (currentCost > dist[u]) continue; | ||
|
||
// 현재 노드의 이웃 노드에 대하여 | ||
for (const Node& neighbor : graph[u]) { | ||
int v = neighbor.vertex; | ||
int c = neighbor.cost; | ||
// 현재 노드까지의 cost + 이웃 노드까지의 cost | ||
int newDist = dist[u] + c; | ||
// 표 값보다 높으면 update (pq에도 반영) | ||
if (newDist < dist[v]) { | ||
dist[v] = newDist; | ||
pq.push({v, newDist}); | ||
} | ||
} | ||
} | ||
return dist; | ||
} | ||
``` | ||
|
||
## 동작 예시 | ||
|
||
```c++ | ||
#include <iostream> | ||
#include <limits> | ||
#include <queue> | ||
#include <vector> | ||
|
||
#include "dijkstra.hpp" | ||
|
||
using namespace std; | ||
|
||
int main() { | ||
int n = 5; // number of vertices | ||
int start = 0; // starting vertex | ||
|
||
vector<vector<Node>> graph(n); // adjacency list | ||
graph[0].push_back({1, 10}); // 0 → 1, 10 | ||
graph[0].push_back({3, 30}); // 0 → 3, 30 | ||
graph[0].push_back({4, 100}); // 0 → 4, 100 | ||
graph[1].push_back({2, 50}); // 1 → 2, 50 | ||
graph[2].push_back({4, 10}); // 2 → 4, 10 | ||
graph[3].push_back({2, 20}); // 3 → 2, 20 | ||
graph[3].push_back({4, 60}); // 3 → 4, 60 | ||
vector<int> shortest_paths = dijkstra(start, n, graph); | ||
|
||
cout << "Shortest distances from vertex " << start << ":\n"; | ||
for (int i = 0; i < n; ++i) { | ||
if (shortest_paths[i] == std::numeric_limits<int>::max()) { | ||
cout << "Vertex " << i << ": INF\n"; | ||
} else { | ||
cout << "Vertex " << i << ": " << shortest_paths[i] << "\n"; | ||
} | ||
} | ||
return 0; | ||
} | ||
``` | ||
|
||
### 그래프 | ||
|
||
예시를 그림으로 표현하면 아래와 같음 | ||
|
||
```mermaid | ||
graph TD; | ||
A((0)) -->|10| B((1)); | ||
A((0)) -->|30| D((3)); | ||
A((0)) -->|100| E((4)); | ||
B((1)) -->|50| C((2)); | ||
D((3)) -->|20| C((2)); | ||
D((3)) -->|60| E((4)); | ||
C((2)) -->|10| E((4)); | ||
``` | ||
|
||
### 탐색 과정 | ||
|
||
#### Step 0. 초기 상태 | ||
|
||
```text | ||
정점: 0 1 2 3 4 | ||
거리: 0 INF INF INF INF | ||
큐: [(0, 0)] | ||
``` | ||
|
||
#### Step 1. 정점 0번 처리 | ||
|
||
`0` → `1, 3, 4` 거리 갱신 | ||
|
||
```text | ||
정점: 0 1 2 3 4 | ||
거리: 0 10 INF 30 100 | ||
큐: [(1, 10), (3, 30), (4, 100)] | ||
``` | ||
|
||
#### Step 2. 정점 1번 처리 | ||
|
||
`1` → `2` 거리 갱신 | ||
|
||
```text | ||
정점: 0 1 2 3 4 | ||
거리: 0 10 60 30 100 | ||
큐: [(3, 30), (4, 100), (2, 60)] | ||
``` | ||
|
||
#### Step 3. 정점 3번 처리 | ||
|
||
`3` → `2, 4` 거리 갱신 | ||
|
||
```text | ||
정점: 0 1 2 3 4 | ||
거리: 0 10 50 30 90 | ||
큐: [(2, 50), (4, 100), (4, 90)] | ||
``` | ||
|
||
#### Step 4. 정점 2번 처리 | ||
|
||
`2` → `4` 거리 갱신 | ||
|
||
```text | ||
정점: 0 1 2 3 4 | ||
거리: 0 10 50 30 60 | ||
큐: [(4, 60), (4, 100), (4, 90)] | ||
``` | ||
|
||
#### Step 5. 정점 4번 처리 | ||
|
||
최단거리 확정 | ||
|
||
```text | ||
정점: 0 1 2 3 4 | ||
거리: 0 10 50 30 60 | ||
``` | ||
|
||
#### 프로그램 실행 결과 | ||
|
||
```text | ||
Vertex 0: 0 | ||
Vertex 1: 10 | ||
Vertex 2: 50 | ||
Vertex 3: 30 | ||
Vertex 4: 60 | ||
``` |