반응형
12-24 00:25
Today
Total
«   2024/12   »
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
관리 메뉴

개발하는 고라니

[알고리즘] Dijkstra 알고리즘 - PriorityQueue 본문

Programming/알고리즘

[알고리즘] Dijkstra 알고리즘 - PriorityQueue

조용한고라니 2021. 1. 28. 22:57
반응형
 

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

최단 경로 최단 경로 문제에서 입력 그래프의 유형은 크게 두 가지이다. 모든 간선 가중치가 음이 아닌 경우 음의 가중치가 존재하는 경우 음의 가중치를 허용하지 않는 경우 다익스트라(Dijkstra)

dev-gorany.tistory.com

이전 포스팅의 다익스트라 알고리즘을 구현한 코드는 O(N^2)의 시간 복잡도를 갖는다. 모든 정점을 한 번씩 순회하는 N번의 loop와 그 안에서 distance[i]의 가장 작은 값의 인덱스를 찾는 N번의 loop가 호출되었다. 단순히 다익스트라 알고리즘을 구현하고 사용하는데에는 코드도 직관적이고 다른 자료구조도 안쓰여서 쉽게 쓸 수 있으나 문제를 풀거나, 서비스에 적용해야 할 때는 문제가 될 여지가 충분하다.

 

 이를 보완하고자 우선 순위 큐(Priority Queue)를 사용하고 인접리스트를 만들어 다익스트라 알고리즘을 구현하면 시간 복잡도를 O(Elog V) 까지 줄일 수 있다. 만약 인접 행렬로 구현했다면 매번 인접한 정점을 찾아야 하므로 루프마다 O(V)의 시간이 소요되어 O(V^2)가 될 것이다.


    static class Edge implements Comparable<Edge> {
        int target, weight;
        public Edge(int t, int w){
            target = t;
            weight = w;
        }

        @Override
        public int compareTo(Edge edge) {
            return this.weight - edge.weight;
        }
    }

간선의 정보를 담을 Edge 클래스이다. Edge 클래스는 target 정점과 가중치 weight를 갖으며, PriorityQueue에 들어가기 위해선 비교할 수 있는 기준이 필요하므로, Comparable 인터페이스를 구현해 우선 순위 기준을 마련했다.

    static void dijkstra(int start){

        distance[start] = 0;

        Q.add(new Edge(start, distance[start]));

        while(!Q.isEmpty()){
            Edge edge = Q.poll(); //우선순위 큐에서 꺼냄

            int current = edge.target; //현재 방문하고 있는 정점
            int weight = edge.weight; //그 가중치

            if(distance[current] < weight) continue; //갱신할 필요가 없으면 skip

            for(int i=0; i<list[current].size(); i++){ //현재 방문한 정점의 인접 정점 iteration
                Edge nextEdge = list[current].get(i);

                int next = nextEdge.target;
                int nextWeight = weight + nextEdge.weight; //갱신할 값

                if(nextWeight < distance[next]){

                    distance[next] = nextWeight;
                    Q.add(new Edge(next, nextWeight));
                }
            }
        }
    }

다익스트라 알고리즘의 소스 코드이다. start정점의 인접리스트에서 원소를 꺼내어 큐에 넣고 while문을 돌린다.

우선순위 큐에서 하나씩 꺼내어 최소 가중치(distance[])를 더 적은 값으로 갱신할 수 있으면 갱신하도록 한다.

# Code </>

public class DijkstraPriorityQ {

    static int n = 6;

    static class Edge implements Comparable<Edge> {
        int target, weight;
        public Edge(int t, int w){
            target = t;
            weight = w;
        }

        @Override
        public int compareTo(Edge edge) {
            return this.weight - edge.weight;
        }
    }

    static Queue<Edge> Q = new PriorityQueue<>();
    static List<Edge>[] list = new ArrayList[n];

    static final int INF = 1000000000;

    static int[] distance = new int[n];

    static void dijkstra(int start){

        distance[start] = 0;

        Q.add(new Edge(start, distance[start]));

        while(!Q.isEmpty()){
            Edge edge = Q.poll(); //우선순위 큐에서 꺼냄

            int current = edge.target; //현재 방문하고 있는 정점
            int weight = edge.weight; //그 가중치

            if(distance[current] < weight) continue; //갱신할 필요가 없으면 skip

            for(int i=0; i<list[current].size(); i++){ //현재 방문한 정점의 인접 정점 iteration
                Edge nextEdge = list[current].get(i);

                int next = nextEdge.target;
                int nextWeight = weight + nextEdge.weight; //갱신할 값

                if(nextWeight < distance[next]){

                    distance[next] = nextWeight;
                    Q.add(new Edge(next, nextWeight));
                }
            }
        }
    }

    public static void main(String[] args) {

        /*
        int[][] arr = {
                {0, 7, 9, INF, INF, 14},
                {7, 0, 10, 15, INF, INF},
                {9, 10, 0, 11, INF, 2},
                {INF, 15, 11, 0, 6, INF},
                {INF, INF, INF, 6, 0, 9},
                {14, INF, 2, INF, 9, 0}
        };
         */

        for(int i=0; i<n; i++)
            distance[i] = INF;

        for(int i=0; i<n; i++)
            list[i] = new ArrayList<>();

        //인접 리스트에 모두 할당
        list[0].add(new Edge(5, 14));
        list[0].add(new Edge(1, 7));
        list[0].add(new Edge(2, 9));


        list[1].add(new Edge(0, 7));
        list[1].add(new Edge(2, 10));
        list[1].add(new Edge(3, 15));

        list[2].add(new Edge(0, 9));
        list[2].add(new Edge(1, 10));
        list[2].add(new Edge(3, 11));
        list[2].add(new Edge(5, 2));

        list[3].add(new Edge(1, 15));
        list[3].add(new Edge(2, 11));
        list[3].add(new Edge(4, 6));

        list[4].add(new Edge(3, 6));
        list[4].add(new Edge(5, 9));

        list[5].add(new Edge(0, 14));
        list[5].add(new Edge(2, 2));
        list[5].add(new Edge(4, 9));

        dijkstra(0);

        Arrays.stream(distance).forEach(i->System.out.print(i + " "));
    }
}

 

# References

동빈나님의 유튜브

안경잡이 개발자님의 블로그

Ries 마법의 슈퍼마리오님의 블로그

반응형
Comments