ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 위상 정렬 알고리즘
    알고리즘 2023. 8. 2. 11:32

    위상 정렬 알고리즘

    위상 정렬(Topology Sort)은 순서가 있는 작업을 차례대로 수행해야 할 때 사용하는 알고리즘이다. 

    위상 정렬을 하기 위해서는 사이클이 없는 방향 그래프 DAG(Directed Acyclic Graph)이여야 한다.

     

    위상정렬은 큐와 각 노드의 진입차수(Indegree)를 이용하여 구현할 수 있다.

    1. 각 Node 별로 Indegree를 구해준다.

    2. Indegree가 0인 Node를 Queue에 넣어준다.

    3. Queue에서 Node를 가져와 해당 Node에서 방문 가능한 Node의 Indegree를 감소시킨다.

    4. 감소된 Node의 Indegree가 0 이라면 Queue에 넣어준다.

    5. Queue가 빌때까지 3,4 작업을 반복한다. 

    위와같은 순서로 동작한다면 Queue에서 방문한 순서대로 위상정렬이 된다.

     

    예시를 통해 살펴보자.

     

    [Step 1] 아래와같이 DAG가 있다. 각 노드별로 Indegree를 구해준 후, Indegree가 0인 Node들을 큐에 넣어준다.

    [Step 2] 큐에서 값을 가져와 해당 node를 방문하고 해당 node에서 가리키는 곳의 Indegree를 감소시킨다. 만약 이 작업으로 Indegree가 0이 된 node가 있다면 다시 큐에 넣어준다. 

    [Step 3] 2번 Node를 큐에서 가져와 방문하고 방문 가능한 곳의 Indegree를 감소시킨다. Node 4의 Indegree 가 0이 되었으니 큐에 새로 넣어준다.

    [Step 4] 큐에서 4번 Node를 가져온후 방문 가능한 곳의 Indegree를 줄여준다. 5번 Node의 Indegree가 0이 되었으니 새로 큐에 넣어준다. 

    [Step 5] 큐에서 5번 Node를 가져온후 방문 가능한 곳의 Indegree를 줄여준다. 3번 Node의 Indegree가 0이 되었으니 새로 큐에 넣어준다. 

    [Step 6] 큐에서 가져온 Node 3번에서는 더이상 방문 가능한 곳이 없다. 큐가 비었으니 종료된다. 따라서 방문 순서는 

    1 - 2 - 4 - 5 - 3 이 된다.

    결과로 나온 방문 순서는 1 - 2 - 4 - 5 - 3 이지만 별다른 조건이 없기에 2 - 1 - 4 - 5 - 3 역시 가능한 결과이다. 

     

    예시 문제

    위상 정렬을 이용하여 백준 1766번 문제를 풀어보자.

    https://www.acmicpc.net/problem/1766

     

    1766번: 문제집

    첫째 줄에 문제의 수 N(1 ≤ N ≤ 32,000)과 먼저 푸는 것이 좋은 문제에 대한 정보의 개수 M(1 ≤ M ≤ 100,000)이 주어진다. 둘째 줄부터 M개의 줄에 걸쳐 두 정수의 순서쌍 A,B가 빈칸을 사이에 두고 주

    www.acmicpc.net

    풀이방법

    위상정렬 개념을 안다면 간단하게 풀 수 있다. 좀 더 생각해야 할 점은 풀어야 할 문제의 우선순위가 있다는 것이다. 이 경우 위의 위상정렬에서 사용되는 큐를 우선순위 큐를 이용하면 풀수 있는 문제들중 우선순위를 정하여 풀 수 있다. 

     

    풀이 코드

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.*;
    
    public class P1766 {
        public static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        public static void main(String[] args) throws IOException {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int N = Integer.parseInt(st.nextToken());
            int M = Integer.parseInt(st.nextToken());
            int[] count = new int[N+1];
            HashMap<Integer,ArrayList<Integer>> graph = new HashMap<>();
    
            for(int i =0; i< M; i++){
                st = new StringTokenizer(br.readLine());
                int prev = Integer.parseInt(st.nextToken());
                int post = Integer.parseInt(st.nextToken());
                count[post] += 1;
                if(graph.get(prev) == null) graph.put(prev, new ArrayList<Integer>());
                graph.get(prev).add(post);
            }
            PriorityQueue<Integer> pq = new PriorityQueue<>();
            for(int i =1; i<count.length; i++){
                if(count[i] == 0){
                    pq.add(i);
                }
            }
            ArrayList<String> answer = new ArrayList<>();
            while(pq.size() > 0){
                int now = pq.poll();
                answer.add(""+now);
                ArrayList<Integer> nowArr = graph.get(now);
                if(nowArr != null){
                    for (int i : nowArr) {
                        count[i]--;
                        if(count[i] == 0) pq.add(i);
                    }
                }
            }
            System.out.println(String.join(" ",answer));
        }
    }

    '알고리즘' 카테고리의 다른 글

    프림 알고리즘  (0) 2024.08.14
    크루스칼 알고리즘  (0) 2024.08.13
    최소 신장 트리  (0) 2024.08.13
Designed by Tistory.