위상 정렬 (Topological Sort)
- 방향 그래프의 정점들을 변의 방향을 거스르지 않도록 나열하는 것을 의미한다.
- 위상 정렬은 순서가 정해져 있는 작업들을 차례대로 수행해야 할 때, 그 순서를 결정해 주는 알고리즘이다.
- 위상 정렬을 가장 잘 설명해 줄 수 있는 예로 교육과정의 선수과목(prerequisite) 구조를 예로 들 수 있다.
- 만약 특정 수강 과목에 선수 과목이 있다면 그 선수 과목부터 수강해야 하므로, 특정 과목들을 수강해야 할 때 위상정렬을 통해 올바른 수강 순서를 찾아낼 수 있다.
- 선후 관계가 정의된 그래프 구조 상에서 선후 관계에 따라 정렬하기 위해 위상 정렬을 이용할 수 있다.
- 정렬의 순서는 방향 그래프의 구조에 따라 여러 개의 종류가 나올 수 있다.
- 위상 정렬이 성립하기 위해서는 반드시 그래프의 순환이 존재하지 않아야 한다.
- 즉, 그래프가 비순환 방향 그래프(directed acyclic graph)여야 한다.
위상 정렬 - 구현 방식
BFS를 이용한 구현
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class TopologicalSortTest_BFS {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
int E = Integer.parseInt(st.nextToken());
List<Integer>[] list = new ArrayList[N + 1];
int[] inDegree = new int[N + 1];
List<Integer> result = new ArrayList<>();
Queue<Integer> queue = new ArrayDeque<>();
for (int i = 1; i <= N; i++) {
list[i] = new ArrayList<>();
}
for (int i = 0; i < E; i++) {
st = new StringTokenizer(br.readLine());
int from = Integer.parseInt(st.nextToken());
int to = Integer.parseInt(st.nextToken());
list[from].add(to);
inDegree[to]++;
}
for (int i = 1; i <= N; i++) {
if(inDegree[i] == 0) queue.add(i);
}
while (!queue.isEmpty()){
int cur = queue.poll();
result.add(cur);
for (int i = 0, end = list[cur].size(); i < end; i++) {
int ad = list[cur].get(i);
if(--inDegree[ad] == 0){
queue.add(ad);
}
}
}
int size = result.size();
if(size == N){
for (int i = 0; i < size; i++) {
System.out.print(result.get(i) + " ");
}
}else System.out.println("cycle");
}
}
DFS를 이용한 구현
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class TopologicalSortTest_DFS {
static List<Integer>[] list;
static List<Integer> result = new ArrayList<>();
static Deque<Integer> stack = new ArrayDeque<>();
static boolean[] visited;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());
int E = Integer.parseInt(st.nextToken());
list = new ArrayList[N + 1];
visited = new boolean[N + 1];
for (int i = 1; i <= N; i++) {
list[i] = new ArrayList<>();
}
for (int i = 0; i < E; i++) {
st = new StringTokenizer(br.readLine());
int from = Integer.parseInt(st.nextToken());
int to = Integer.parseInt(st.nextToken());
list[from].add(to);
}
dfs();
int size = result.size();
if(size == N){
for (int i = 0; i < size; i++) {
System.out.print(result.get(i) + " ");
}
}else System.out.println("cycle");
}
static void dfs(){
stack.push(1);
visited[1] = true;
while (!stack.isEmpty()) {
int now = stack.pop();
result.add(now);
for (Integer i : list[now]) {
if(visited[i]) continue;
visited[i] = true;
stack.push(i);
}
}
}
}