[Data Structure] 최단경로 - Dijkstra Algorithm
2021. 12. 25. 14:32ㆍMajor`/자료구조
최단경로
- 정점 i ~ 정점 j까지 간선들의 weight 합이 최소가 되는 경로
Dijkstra
- 하나의 시작 정점 ~ 모든 다른 정점까지의 최단 경로
Floyd
- 모든 정점 ~ 다른 모든 정점까지의 최단 경로
Dijkstra Algorithm
- 탐욕적 방법 (greedy method) 이용
- 특정 시작 정점 ~ 다른 모든 정점까지의 최단 경로 계산
- 가중치가 음수가 아닐 때 정상적으로 작동
- Dijkstra → Greedy → DP(최단경로 + 길찾기) / 매 상황에서 가장 비용이 적은 최선의 정점 선택
- 시작정점으로부터 거리를 저장하는 distance[] 1차원 배열이 필요
- O(n²)
- 시작 정점(v) 선택
- 시작 정점으로부터 다른 모든 정점까지의 distance 업데이트 (한 번에 갈 수 없으면 INF로 초기화)
- INF가 아닌 인접 정점 중, 최단거리의 정점(w) 선택
- v~w를 거쳐서 다른 모든 정점(s)까지의 distance 업데이트 → distance[s], distance[w] + distance[w][s] 중 더 작은 값으로 update (v~s(한 번에) or v~w~s(w를 거쳐서)
※ Example
Vertex | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
distance | INF | INF | INF | INF | INF | INF | INF |
select | X | X | X | X | X | X | X |
- 시작 정점 : 0
- 시작 정점에서의 가중치 = 0으로 설정 (g->weight[start][start] = 0)
Vertex | 1 | 2 | 3 | 4 | 5 | 6 | |
distance | 15 | INF | 54 | 24 | INF | INF | |
select | X | X | X | X | X | X |
- 0에서부터 weight가 가장 작은 정점 1 선택
- 이때, 0~1~3 weight(52)가 0~3 weight(54)보다 작기 때문에 distance[3]을 더 작은 값으로 update
Vertex | 2 | 3 | 4 | 5 | 6 | ||
distance | INF | 52 | 24 | INF | 58 | ||
select | X | X | X | X | X |
- 0에서부터 weight가 가장 작은 정점 4 선택
- 0~4~2 weight(86) < 0~2 weight(INF) / 0~4~5 weight(43) < 0~5 weight(INF)
Vertex | 2 | 3 | 5 | 6 | |||
distance | 86 | 52 | 43 | 58 | |||
select | X | X | X | X |
- 0에서부터 weight가 가장 작은 정점 5 선택
- 0~5~2 weight(60) < 0~2 weight(86)
Vertex | 2 | 3 | 6 | ||||
distance | 60 | 52 | 58 | ||||
select | X | X | X |
- 0에서부터 weight가 가장 작은 정점 3 선택
Vertex | 2 | 6 | |||||
distance | 60 | 58 | |||||
select | X | X |
- 0에서부터 weight가 가장 작은 정점 6 선택
Vertex | 2 | ||||||
distance | 60 | ||||||
select | X |
- 0에서부터 weight가 가장 작은 정점 2 선택
Vertex | |||||||
distance | |||||||
select |
- 정점 0으로부터 각 정점까지의 최단경로 탐색 알고리즘 종료
▶ Dijkstra Algorithm Code
int distance[MAX_VERTEX];
int selected[MAX_VERTEX];
void init_distance(int distance[]) {
for (int i = 0; i < MAX_VERTEX; i++)
distance[i] = INF;
}
void init_selected(int selected[]) {
for (int i = 0; i < MAX_VERTEX; i++)
selected[i] = FALSE;
}
- distance, selected 배열을 각각 INF, FALSE로 초기화
#define min(a, b) ((a<b)?a:b)
int select_min(int n) {
int min = INF;
int v=0;
for (int i = 0; i <n; i++) {
if (selected[i] == FALSE && distance[i] < min) {
min = distance[i];
v = i;
}
}
return v;
}
void dijkstra(graph* g, int v) {
init_distance(distance);
init_selected(selected);
int w, s; // w = v의 인접 정점, s = w의 인접 정점
distance[v] = 0;
g->weight[v][v] = 0;
for (int i = 0; i < g->n; i++) {
distance[i] = g->weight[v][i];
}
for (int i = 0; i < g->n; i++) {
w = select_min(g->n);
selected[w] = TRUE;
printf("\n");
printf("Step %d : 정점 %d 선택\n", i+1, w);
for (s = 0; s < g->n; s++) {
if (g->weight[w][s] != INF && selected[s] == FALSE) {
distance[s] = min(distance[s], distance[w] + g->weight[w][s]);
}
}
printf("%-8s >> [ ", "distance");
for (int i = 0; i < g->n; i++) {
if (distance[i] == INF)
printf("%s ", "*");
else
printf("%d ", distance[i]);
}
printf("]\n");
printf("%-8s >> [ ", "select");
for (int i = 0; i < g->n; i++) {
if (selected[i] == TRUE)
printf("%s ", "O");
else
printf("%s ", "X");
}
printf("]\n");
}
printf("--------------------------------------------\n");
}
Code 6~12
- 선택되지 않은 정점들에 대해, 가장 작은 weight를 가지고 있는 정점 탐색 후 return
Code 21~22
- 함수의 매개변수 v는 시작 정점이기 때문에, distance[v] = 0으로 설정해주고, 해당 정점의 weight (g->weight[v][v])또한 0으로 설정해준다
Code 24~26
- 시작 정점으로부터 모든 정점들에 대해 각각 distance 설정
- edge가 존재하면 해당 weight
- edge가 없으면 INF
Code 29~30
- 선택되지 않은 정점 중 weight가 가장 작은 정점을 선택하고, 해당 정점은 selected - TRUE로 표시
Code 34~38
- Code 27~28에서 선택한 정점(w)로부터 edge가 존재하고, 선택되지 않은 모든 정점들 s에 대해서 weight(v~s), weight(v~w~s)중, 더 짧은 weight를 새로 갱신
- distance[s] = min(distance[s], distance[w] + g->weight[w][s])
▶ Full Code
#include <stdio.h>
#include <stdlib.h>
// Dijkstra ShortestWay Algorithm
#define TRUE 1
#define FALSE 0
#define MAX_VERTEX 100
#define INF 99999
#define min(a, b) ((a<b)?a:b)
int distance[MAX_VERTEX];
int selected[MAX_VERTEX];
void init_distance(int distance[]) {
for (int i = 0; i < MAX_VERTEX; i++)
distance[i] = INF;
}
void init_selected(int selected[]) {
for (int i = 0; i < MAX_VERTEX; i++)
selected[i] = FALSE;
}
typedef struct graph {
int n; // 정점 개수
int vertex[MAX_VERTEX];
int weight[MAX_VERTEX][MAX_VERTEX];
}graph;
graph* create() {
return (graph*)malloc(sizeof(graph));
}
void init_graph(graph* g) {
g->n = 0;
for (int r = 0; r < MAX_VERTEX; r++) {
for (int c = 0; c < MAX_VERTEX; c++) {
g->weight[r][c] = INF;
}
}
}
int is_full(graph* g) {
return g->n == MAX_VERTEX;
}
int bool_vertex(graph* g, int v) {
int flag = FALSE;
for (int i = 0; i < g->n; i++) {
if (g->vertex[i] == v)
flag = TRUE;
}
if (flag == TRUE) return TRUE;
else return FALSE;
}
void insert_vertex(graph* g, int v) {
if (is_full(g))
return;
else if (bool_vertex(g, v) == TRUE)
return;
g->vertex[g->n++] = v;
}
void insert_edge(graph* g, int start, int end, int weight) {
// 무방향 그래프를 조건으로
if (bool_vertex(g, start) == FALSE || bool_vertex(g, end) == FALSE)
return;
g->weight[start][end] = weight;
g->weight[end][start] = weight;
}
int select_min(int n) {
int min = INF;
int v=0;
for (int i = 0; i <n; i++) {
if (selected[i] == FALSE && distance[i] < min) {
min = distance[i];
v = i;
}
}
return v;
}
void dijkstra(graph* g, int v) {
init_distance(distance);
init_selected(selected);
int w, s; // w = v의 인접 정점, s = w의 인접 정점
distance[v] = 0;
g->weight[v][v] = 0;
for (int i = 0; i < g->n; i++) {
distance[i] = g->weight[v][i];
}
for (int i = 0; i < g->n; i++) {
w = select_min(g->n);
selected[w] = TRUE;
printf("\n");
printf("Step %d : 정점 %d 선택\n", i+1, w);
for (s = 0; s < g->n; s++) {
if (g->weight[w][s] != INF && selected[s] == FALSE) {
distance[s] = min(distance[s], distance[w] + g->weight[w][s]);
}
}
printf("%-8s >> [ ", "distance");
for (int i = 0; i < g->n; i++) {
if (distance[i] == INF)
printf("%s ", "*");
else
printf("%d ", distance[i]);
}
printf("]\n");
printf("%-8s >> [ ", "select");
for (int i = 0; i < g->n; i++) {
if (selected[i] == TRUE)
printf("%s ", "O");
else
printf("%s ", "X");
}
printf("]\n");
}
printf("--------------------------------------------\n");
}
int main(void) {
graph* g;
g = create(); init_graph(g);
for (int i = 0; i < 7; i++)
insert_vertex(g, i);
insert_edge(g, 0, 1, 15);
insert_edge(g, 0, 3, 54);
insert_edge(g, 0, 4, 24);
insert_edge(g, 1, 3, 37);
insert_edge(g, 1, 4, 77);
insert_edge(g, 1, 6, 43);
insert_edge(g, 2, 4, 62);
insert_edge(g, 2, 5, 17);
insert_edge(g, 2, 6, 45);
insert_edge(g, 3, 6, 31);
insert_edge(g, 4, 5, 19);
printf("--------------------------------------------\n");
printf("Dijkstra Algorithm\n");
printf("--------------------------------------------\n");
printf("시작 정점 : 0\n");
dijkstra(g, 0);
printf("시작 정점 : 2\n");
dijkstra(g, 2);
return 0;
}