본문 바로가기

자료구조 알고리즘/탐색 알고리즘(Search)

[알고리즘] 다익스트라 알고리즘(Dijkstra algorithm)

 

 

 

 

 

알고리즘 개요 및 소개

다익스트라 알고리즘

시간 복잡도 O(|E|+|V| log |V|)
장점 최단 거리를 알 수 있다.
단점 음의 경우의 수를 포함하지 않는다.

다익스트라 알고리즘, 벨만-포드 알고리즘, 사이클이 없는 그래프의 최단경로 알고리즘은 하나의 시작 정점으로부터 다른 모든 정점까지의 최단경로를 구한다. 임의의 두 정점 간에 경로가 존재하지 않으면 이 경로의 길이는 INFINITE(무한)으로 정의한다. 또 두 정점 사이에 간선이 없으면 가중치가 무한인 간선이 있다고 간주한다. 

다익스트라 알고리즘은 간선에 음의 가중치를 허용하지 않는대 그 이유는 시작점에서 그 정점까지의 최단 거리는 계산이 끝났다는 확신을 가지고 더하기 때문이고 만약 이후에 더 짧은 경로가 존재한다면 다익스트라 알고리즘의 논리가 깨지기 때문이다.

 

 

 

탐색 방법

초기 상태

먼저 모든 정점은 아직 방문하지 않았기 때문에 INF로 처리한다.

시작 노드를 A로 지정

A노드를 시작 노드로 하고 처음에 연결된 모든 노드를 방문하고 그 노드의 가중치가 더 작다면 해당하는 값으로 바꾼다. 여기서는 모두 INF이기 때문에 값이 바뀌게 된다.

 

D노드를 방문

그 후 방문하지 않은 노드 중에서 가장 가중치가 낮은 노드를 선택한다 여기에서는 D를 선택하였고 똑같이 D에 연결된 노드를 방문한다. 

여기서 E의 가중치 값이 6으로 바뀌었는대 기존 A-D(5)에 D-E(1)을 더한 값인 6이 들어가게 된다.

 

E노드를 방문

그 후 방문하지 않는 노드들 중에서 가장 가중치가 낮은 E노드를 방문한다.

여기서 B노드의 가중치는 12이지만 A-D-E 노드를 거치는 과정에서 8이라는 더 작은 숫자가 나왔기 때문에 바꾼다. C노드도 동일한 이유로 9로 값이 바뀐다.

 

남은 C와 B노드 탐색

C와 B 노드는 인접노드들 중 방문하지 않는 노드가 없기 때문에 그대로 종료된다.

 

 

소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include<iostream>
#include<vector>
#include<queue>
constexpr int INF = 2147483647;
constexpr int MAX_V =5;
enum NODE{A=0,B,C,D,E};
 
class Node {
public:
    int v_;
    int distance_;
    Node():v_(0), distance_(0) {};
    Node(int v, int distance) :v_(v), distance_(distance) {};
 
    constexpr bool operator<(const Node& rhs)const{
        return distance_ > rhs.distance_;
    }
};
 
std::vector<Node>v[MAX_V];
int distance[MAX_V];
 
void Dijkstra(int started) {
    std::priority_queue<Node> queue//가장 작은 간선치만 꺼내기 위해 사용
    distance[started] = 0;
    queue.emplace(Node{ started, 0 });
    while (queue.empty() == false) {
 
        auto top = queue.top();
        queue.pop();
 
        int n = top.v_; //노드
        int d = top.distance_; //거리
 
        //해당 노드와 연결된 노드의 값을 갱신한다.
        for (auto node : v[n]) {
        
            int aroundNode = node.v_;
            int aroundDistance = node.distance_ + d;
 
            //현재 노드의 거리값이 더 작다면 갱신한다.
            if (aroundDistance <= distance[aroundNode]) {
                distance[aroundNode] = aroundDistance;
                queue.emplace(Node{ aroundNode, aroundDistance });
            }
        }
    }
 
}
 
int main() {
 
    v[A].emplace_back(Node{ B,12 });
    v[A].emplace_back(Node{ C,10 });
    v[A].emplace_back(Node{ D,5 });
 
    v[B].emplace_back(Node{ A,12 });
    v[B].emplace_back(Node{ D,13 });
    v[B].emplace_back(Node{ E,2 });
 
 
    v[C].emplace_back(Node{ A,10 });
    v[C].emplace_back(Node{ E,3 });
 
    v[D].emplace_back(Node{ A,5 });
    v[D].emplace_back(Node{ B,13 });
    v[D].emplace_back(Node{ E,1 });
 
    v[E].emplace_back(Node{ B,2 });
    v[E].emplace_back(Node{ C,3 });
    v[E].emplace_back(Node{ D,1 });
 
    for (int i = 0; i < MAX_V; ++i)
        distance[i] = INF;
 
    Dijkstra(A);
 
    for (int i = 0; i < MAX_V; ++i) {
        
        std::cout << static_cast<char>('A'+i)<<" Distance: "<<distance[i]<< std::endl;
    }
 
 
}
 

결과

후기

다익스트라 알고리즘을 구현해보았다. 생각보다 알고리즘이 간단해서 놀랐다. 음의 가중치를 포함하지 않는 알고리즘이라는 단점이 있지만 현실에서는 음의 가중치가 없기 때문에 실용적인 알고리즘이기도 하다.

길 찾기에 마지막은 A* 알고리즘을 해볼 예정이며 이걸 게임에 적용까지 해보는게 탐색 알고리즘의 최종 목표이다.

 

 

 

출처 및 레퍼런스

Wiki: https://ko.wikipedia.org/wiki/데이크스트라_알고리즘

Book: 쉽게 배우는 알고리즘 관계 중심의 사고법 저) 문병로 

그림: 본인

 

*2020.04.05 

code를 Color Scripter로 변경

 

관련 글

[자료구조 알고리즘/탐색 알고리즘(Search)] - [알고리즘] 깊이 우선 탐색(Depth First Search)

[자료구조 알고리즘/탐색 알고리즘(Search)] - [알고리즘] 너비 우선 탐색(Breadth First Search)

[자료구조 알고리즘/탐색 알고리즘(Search)] - [알고리즘] A* 알고리즘