Software Courses/Data Structure

Shortest path (최단 경로)

김 정 환 2020. 12. 22. 17:20
반응형

최단 경로 문제

    - 그래프 이론에서 두 정점을 잇는 가장 짧은 경로를 찾는 문제

 

 

 

문제 종류

    - 단일-쌍 최단 경로 문제 : V에서 V'로 가는 경로들 중 최단 경로 찾기

    - 단일-출발 최단 경로 문제 : V에서 출발하여 그래프 내의 모든 정점에 도착하는 최단 경로 찾기

    - 단일-도착 최단 경로 문제 : 그래프 내의 모든 정점에서 출발하여 V에 도착하는 최단 경로 찾기

    - 전체-쌍 최단 경로 문제 : 그래프 내의 모든 정점 사이의 최단 경로 찾기

 

 

 

알고리즘 종류

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

    - 그래프 이론에서 하나의 시작 정점에서 다른 모든 정점으로 가는 최단 경로를 찾는 알고리즘

 

    - 동작

        * 출발 노드 선택

        * U노드로 이동할 때, 현재 이동해서 U까지 온 비용과 이전에 저장된 U까지 온 비용을 비교하여 최소 비용을 U노드까지 오는 비용으로 저장

        * 다른 인접 노드로 이동

 

    - 예시

 

 

        * 출발 노드로 A를 선택

 

 

        * 배열에 저장된 비용 중에 아직 방문하지 않았고 비용이 가장 적은 B 노드 선택

        * 노드 B를 거쳐서 모든 노드에 도착하는 비용 VS 이미 저장된 비용 하여 최소 비용을 저장

            + 노드 D의 경우

                ~ 노드 B를 거쳐가는 경우 : 7 + 15 = 22

                ~ 이미 저장된 비용 : INF

                ~ 22가 더 비용이 작으므로 A->D로 가는 비용을 22로 변경

 

 

        * 배열에 저장된 비용 중에 아직 방문하지 않았고 비용이 가장 적은 C 노드 선택

        * 노드 C를 거쳐서 모든 노드에 도착하는 비용 VS 이미 저장된 비용 하여 최소 비용을 저장

            + 노드 D의 경우

                ~ 노드 C를 거쳐가는 경우 : 9 + 11 = 20

                ~ 이미 저장된 비용 : 22

                ~ 20가 더 비용이 작으므로 A->D로 가는 비용을 20로 변경

            + 노드 F의 경우

                ~ 노드 C를 거쳐가는 경우 : 9 + 2 = 11

                ~ 이미 저장된 비용 : 14

                ~ 11이 더 비용이 작으므로 A->F로 가는 비용을 11로 변경

 

 

        * 배열에 저장된 비용 중에 아직 방문하지 않았고 비용이 가장 적은 F 노드 선택

        * 노드 F를 거쳐서 모든 노드에 도착하는 비용 VS 이미 저장된 비용 하여 최소 비용을 저장

            + 노드 E의 경우

                ~ 노드 F를 거쳐가는 경우 : 11 + 9 = 20

                ~ 이미 저장된 비용 : INF

                ~ 20가 더 비용이 작으므로 A->E로 가는 비용을 20로 변경

 

 

        * 배열에 저장된 비용 중에 아직 방문하지 않았고 비용이 가장 적은 F 노드 선택

        * 노드 F를 거쳐서 모든 노드에 도착하는 비용 VS 이미 저장된 비용 하여 최소 비용을 저장

            + 최소 비용 변경 없음

 

 

    - 구현

        * 전체 그래프를 2차 배열에 저장하게 되면 정점을 모두 순회하기 때문에 시간복잡도 O(N^2)을 가짐

        * 정점 선택 기준은 최소 비용이었으므로 최소 힙을 이용하여 데이터를 저장하면 시간복잡도 O(N*logN)을 가짐

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
85
86
87
88
89
90
91
92
#include <iostream>
#include <vector>
#include <queue>
 
using namespace std;
 
int n = 6// 정점의 개수  
int INF = 987654321;
 
 
vector<pair<intint> > edges[9]; // 간선 정보  
int distance[10]; // 간선 비용
 
 
void dijkstra(int start){
    
    //자기 자신까지 비용은 0 
    distance[start] = 0;
    
    // 힙 구조
    priority_queue<pair<intint> > pq;
    
    // 정점과 비용을 저장  
    pq.push({start, 0})
    
    while(!pq.empty()){
        
        int cur_node = pq.top().first;
        // 힙에는 최소값이 먼저 오도록 하기 위해서
        // -1를 곱해서 저장된 상태
        // 따라서 꺼낼 때는 다시 -1을 곱하여 양수화  
        int cur_distance = -pq.top().second; 
        pq.pop();
        
        // 이미 저장된 거리가 더 최소 비용이면 스킵  
        if(distance[cur_node] < cur_distance) continue;
        
        // 선택한 현재 노드와 연결된 인접 노드들 순회  
        for(int i=0; i<edges[cur_node].size(); i++){
            
            // 선택한 노드의 인접 노드  
            int next_node = edges[cur_node][i].first;
            // 선택한 노드를 거쳐 인접 노드로 가는 비용  
            int next_distance = distance + edges[cur_node][i].second;
            
            // 이미 저장된 최소 비용보다 더 최소 비용이면 저장  
            if(next_distance < distance[next_node]){
                distance[next_node] = next_distance;
                pq.push({next_node, -next_distance});
            }
        }
    }
}
 
 
int main(void){
    
    //초기화 
    for (int i=1; i<=n; i++
        distance[i] = INF;
    
    edges[1].push_back({2,7});
    edges[1].push_back({3,9});
    edges[1].push_back({6,14});
    
    edges[2].push_back({1,7});
    edges[2].push_back({3,10});
    edges[2].push_back({4,15});
    
    edges[3].push_back({1,9});
    edges[3].push_back({2,10});
    edges[3].push_back({4,11});
    edges[3].push_back({6,2});
    
    edges[4].push_back({2,15});
    edges[4].push_back({3,11});
    edges[4].push_back({5,6});
    
    edges[5].push_back({4,6});
    edges[5].push_back({6,9});
    
    edges[6].push_back({1,14});
    edges[6].push_back({3,2});
    edges[6].push_back({5,9});
     
    dijkstra(1);
    
    // 결과 보기
    for(int i=1; i<=n; i++)
        printf("%d", distance[i]); 
    
}
cs

 

    - 시간복잡도

        * 힙 구조에 데이터를 저장하면, O(N * logN)

        * 2차원 배열에 데이터를 저장하면, O(N^2)

 

 

 

2) Floyd-Warshall (플로이드-와셜 알고리즘)

    - 그래프 이론에서 모든 정점에서 다른 모든 정점으로 가는 최단 경로를 찾는 알고리즘

 

    - 동작

        * 거쳐갈 정점 선택

        * U 노드를 거쳐서 갈 때의 비용 VS U 노드를 거쳐서 가지 않을 때 비용

 

    - 예시

 

 

        * 정점 E에서 A를 거쳐 B로 가는 비용은 21(14+7)이므로 INF보다 작기 때문에 갱신

 

        * 정점 A에서 B를 거쳐 C로 가는 비용은 22(7+15)이므로 INF보다 작기 때문에 갱신

        * 정점 A에서 B를 거쳐 E로 가는 비용은 28(7+21)이므로 INF보다 작기 때문에 갱신

        * 이하 같은 방식

 

        * 이하 같은 방식으로 갱신

 

 

    - 구현

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
#include <stdio.h>
 
using namespace std;
 
int n = 5;
int INF = 987654321;
 
int mat[5][5]= {
        {07, INF, INF, INF},
        {INF, 015, INF, 21},
        {INF, INF, 06, INF},
        {113, INF, 0, INF},
        {14, INF, 1390}
    };
 
void FW(){
    
    for(int k=0; k<n; k++){
        for(int i=0; i<n; i++){
            for(int j=0; j<n; j++){
                
                if(mat[i][k] + mat[k][j] < mat[i][j])
                    mat[i][j] = mat[i][k] + mat[k][j];
                
            }
        }
    }
    
}
 
int main(void){
    
    FW();
    
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            printf("%3d ", mat[i][j]);
        }
        printf("\n");
    }
}
cs

 

    - 시간복잡도

        * 정점을 3개의 for문으로 순회하기 때문에 O(N^3)

 

 

참고

    - 블로그 1

    - 블로그 2

    - 위키피디아

    - 블로그 3

    - 블로그 4

    - 위키피디아

반응형

'Software Courses > Data Structure' 카테고리의 다른 글

Insertion sort (삽입 정렬)  (0) 2020.12.22
Selection sort(선택 정렬)  (0) 2020.12.22
MST(Minimum Spanning Tree) 최소 신장 트리  (0) 2020.12.21
Union-Find  (0) 2020.12.21
Graph  (0) 2020.12.21