알고리즘

(Java) 위상정렬 (feat. 백준 2252 줄 세우기)

뽀루피 2024. 8. 6. 13:45

위상정렬

위상정렬은 DAG(Directed Acyclic Graph, 방향성이 있으며 사이클이 없는 그래프)에서 순서를 정렬하는 알고리즘이다.

 

위상정렬을 구현하기 위해서는 4가지 변수를 만들어줘야 한다.

  • int[] indegree : 특정 노드에 대해서 다른 노드로부터 들어오는 간선의 개수
  • List<List<Integer>> array : 그래프의 간선 정보를 담은 2차원 리스트
  • Queue<Integer> q : indegree 값이 0이 된 노드들을 담기 위한 queue
  • Queue<Integer> result : 정렬된 순서를 보여줄 결과 queue. q에서 빠져나온 순서대로 정렬된다.

 

1. 맨 처음 indegree 값이 0인 노드들을 Queue에 담고 시작한다.

 

2. Queue에서 노드 1을 빼주며 1에서 가르킨 노드들의 indegree 값을 1씩 감소시킨다.

즉, 노드 1부터 시작하는 간선을 지워준다.

그 다음 1 노드를 Result에 추가해준다.

그리고 2와 3 노드가 새롭게 indegree가 0이 됐으므로 Queue에 2, 3을 추가해준다.

 

3. Queue에서 노드 2를 빼주며 2에서 가르킨 노드 4의 indegree 값을 1 감소시킨다.

그 다음 2 노드를 Result에 추가해준다.

그리고 4 노드의 indegree가 0이 됐으므로 Queue에 4를 추가해준다.

 

4. Queue에서 노드 3를 빼주며 3에서 가르킨 노드 5의 indegree 값을 1 감소시킨다.

그 다음 3 노드를 Result에 추가해준다.

그리고 5 노드의 indegree가 0이 됐으므로 Queue에 5를 추가해준다.

 

5. Queue에서 노드 4를 빼주며 4에서 가르킨 노드 6, 7의 indegree 값을 1 감소시킨다.

그 다음 4 노드를 Result에 추가해준다.

 

6. Queue에서 노드 5를 빼주며 5에서 가르킨 노드 6의 indegree 값을 1 감소시킨다.

그 다음 5 노드를 Result에 추가해준다.

그리고 6 노드의 indegree가 0이 됐으므로 Queue에 6를 추가해준다.

 

7. Queue에서 노드 6를 빼주며 6에서 가르킨 노드 7의 indegree 값을 1 감소시킨다.

그 다음 6 노드를 Result에 추가해준다.

그리고 7 노드의 indegree가 0이 됐으므로 Queue에 7를 추가해준다.

 

8. 7 노드를 Result에 추가해준다.

 

 

최종적으로 Queue는 비워지고 Result에 들어간 순서가 위상 정렬된 결과값이다.

위상 정렬은 어려가지 결과값이 존재할 수 있다.

하지만 화살표가 가르키는 순서는 지켜져야 한다.

 

 

위상정렬 알고리즘을 이용하여 문제를 풀어보겠다.

 

 

백준 2252 줄 세우기

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

 

N명의 학생들을 키 순으로 정렬하고자 한다.

입력 조건으로 두 명간의 키를 비교한 결과값만 주어진다.

M 번 키를 비교하는데 두 학생 A, B의 번호가 주어진다.

이는 A가 B의 앞에 서야한다는 의미이다.

 

import java.io.*;
import java.util.*;

public class Main {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        st = new StringTokenizer(br.readLine());
        int N = Integer.parseInt(st.nextToken());   // 학생 수
        int M = Integer.parseInt(st.nextToken());   // 키를 비교한 횟수

        List<Integer> BArr[] = new ArrayList[N+1];  // 간선 정보 담은 배열 + 리스트
        for(int i = 0; i <= N; i++) {
            BArr[i] = new ArrayList<>();
        }

        int indegree[] = new int[N+1];              // 다른 노드로부터 들어오는 간선의 개수
        for(int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int A = Integer.parseInt(st.nextToken());
            int B = Integer.parseInt(st.nextToken());

            BArr[A].add(B);
            indegree[B]++;
        }

        Queue<Integer> result = new LinkedList<>();
        Queue<Integer> q = new LinkedList<>();
        for(int i = 1; i <= N; i++) {
            if(indegree[i] == 0) {
                q.add(i);
            }
        }

        while(!q.isEmpty()){
            int node = q.poll();
            result.add(node);
            for(int i = 0; i < BArr[node].size(); i++) {
                int tmp = BArr[node].get(i);
                indegree[tmp]--;
                if(indegree[tmp] == 0) {
                    q.add(tmp);
                }
            }
        }

        StringBuilder sb = new StringBuilder();
        while(!result.isEmpty()) {
            int tmp = result.poll();
            sb.append(tmp + " ");
        }

        System.out.println(sb);
    }
}