CS/알고리즘

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

내꺼블로그 2024. 4. 8. 11:14

다익스트라(Dijkstra) 알고리즘이란?

최단 경로를 구하는 알고리즘 중 하나.

단일 소스(하나의 노드)로부터 출발하여 나머지 노드까지 가는 데 최단 경로를 구하는 방법이다.

단, 음수 가중치가 있을 경우에는 사용 불가

 


 

알고리즘 동작 방식

다익스트라 알고리즘은 하나의 노드에서 그 노드와 가장 가까운 경로를 fix하는 데서 출발한다.

최소 신장 트리를 찾는 알고리즘 중 prim 알고리즘과 유사한데, 차이점이 있다면 prim은 현재 노드에서 가중치가 적은 노드를 찾는 반면 다익스트라는 시작 노드로부터 가중치가 적은 경로의 노드를 골라야 한다는 데에 있다.

 

좀 더 쉬운 이해를 위해 예시를 보도록 하자.

0 1 2 3 4 5 6
0 4 2 1 INF INF INF

 

시작노드 0을 기준으로 나머지 노드로의 가중치(최소값)는 위의 표와 같다.

0: 자기 자신이므로 가중치의 값은 0이 된다.

1: 0노드에서 바로 갈 수 있는 경로의 가중치이므로 4가 된다.

2: 0노드에서 바로 갈 수 있는 경로의 가중치이므로 2가 된다.

3: 0노드에서 바로 갈 수 있는 경로의 가중치이므로 1이 된다.

4, 5, 6: 0노드와 아직 직접적으로 이어져 있진 않으므로 가중치를 알 수 없기에 INF

 

위의 가중치 중 가장 작은 값이 1이므로 0->3 경로는 fix한다.

0 1 2 3 4 5 6
0 3 2 1 INF 8 INF

 

이제는 0 뿐만 아니라 3노드에서 갈 수 있는 경로까지 고려할 수 있다.

따라서 이전 표에서 1노드와 5노드로 가는 경로의 가중치값(최소값)이 변경이 된다.

1노드의 경우, 3노드를 거쳐 가는 경로의 가중치 3(1+2)이 0노드에서 바로 가는 경로의 가중치 4보다 작은 값이므로 표의 값을 변경해준다.

5노드의 경우도 마찬가지로 3노드를 거쳐 5노드로 가는 경로가 보이므로 가중치를 INF에서 8(1+7)로 변경해준다.

 

위의 값들 중 0과 3노드로의 경로를 제외하고 가장 가중치가 낮은 경로는 2노드로 가는 경로이므로 이 경로또한 fix해준다.

0 1 2 3 4 5 6
0 3 2 1 5 8 INF

 

2노드에서의 경로까지 고려해도 되자 가중치를 계산할 수 있는 노드가 하나 더 늘었다.

4노드는 0->2->4를 거칠 수 있으므로 4노드로 가는 경로의 가중치를 INF에서 5(2+3)로 변경해준다.

 

0, 3, 2노드가 fix된 상태에서 가중치가 가장 적은 경로는 1노드로 가능 경로이므로 1노드로의 경로를 fix해준다.

0 1 2 3 4 5 6
0 3 2 1 5 8 INF

 

fix된 경로를 제외하고 가장 작은 가중치의 경로는 4노드로 가는 경로이므로 이를 fix한다.

0 1 2 3 4 5 6
0 3 2 1 5 8 INF

 

5노드로 가는 경로는 0->2->4->5와 0->3->5 경로 2가지가 있다.

전자의 경우 가중치 값이 9인 반면, 후자의 경우 가중치 값이 8이므로 현재 값을 변경하지는 않는다.

 

남은 경로 중 가중치 값이 가장 적은 경로가 5노드로 가는 경로이므로 이 또한 fix한다.

0 1 2 3 4 5 6
0 3 2 1 5 8 9

 

5노드를 fix하면서 6노드로 가는 경로의 가중치를 계산할 수 있게 되었다.

'5노드로의 가중치 + 5노드에서 6노드로 가는 데 드는 가중치' 이므로 9가 된다.

 

이제 남은 경로는 6노드로 가는 경로 하나뿐이므로 이를 fix한다.

0 1 2 3 4 5 6
0 3 2 1 5 8 9

 

일련의 과정을 거쳐 나온 이 상태가 다익스트라로 얻은 0노드로부터의 최단 경로가 된다.

 


 

코드 작성

이제 기본적인 동작 방식은 확인했으니 이를 코드로 작성해보자.

 

 

1) 순차탐색

시간복잡도 O(n^2)가 요구되는 구현이다.

방문하지 않은 노드 중 최단 경로인 노드를 찾는 방법을 순차탐색으로 진행한 것이다.

최단 경로인 노드를 찾기 위해 n만큼의 값 비교와 방문 여부를 확인해줘야 하며 그 노드에서 n-1개의 노드로 가는 최단 경로를 갱신해줘야 한다.

using System;

namespace ConsoleApp
{
    internal class Program
    {
        const int INF = 100000000;
        static void Main(string[] args)
        {
            int[,] weight =
            {
                {0,4,2,1, INF, INF, INF},
                {4,0,INF,2,INF,INF,INF },
                {2,INF,0,INF,3,INF,INF },
                {1,2,INF,0,INF,7,INF },
                {INF,INF,3,INF,0,4,INF },
                {INF,INF,INF,7,4,0,1 },
                {INF,INF,INF,INF,INF,1,0 }
            };
            int startNode = 0;
            int[] dist = new int[7];
            for (int i = 0; i < dist.Length; i++)
                dist[i] = weight[0, i];
            bool[] visited = { false, false, false, false, false, false, false };
            Dijkstra(weight, dist, visited, startNode);
            for(int i = 0; i < dist.Length; i++)
            {
                Console.WriteLine("{0}: {1}", i, dist[i]);
            }
        }
        static void Dijkstra(int[,] w, int[] d, bool[] v, int s)
        {
            for(int i = 0; i < d.Length; i++)
            {
                int node = GetNode(d, v);
                int dist = d[node];
                v[node] = true;
                for(int j = 0; j < w.GetLength(1); j++)
                {
                    if (dist + w[node, j] < d[j])
                    {
                        d[j] = dist + w[node, j];
                    }
                }
            }
        }

        static int GetNode(int[] d, bool[] v)
        {
            int nodeIdx = 0;
            int minWeight = INF;
            for(int i = 0; i < d.Length; i++)
            {
                if (minWeight > d[i] && !v[i])
                {
                    minWeight = d[i];
                    nodeIdx = i;
                }
            }
            return nodeIdx;
        }
    }
}

 

 

결과