방향 그래프에서 정점(vertex)을 간선(edge)의 방향에 거스르지 않도록 나열하는것을 말한다.
위상정렬은 선후 관계가 정의된 그래프 구조상에서 선후 관계에 따른 정렬에 사용될 수 있다. 대표적으로 대학교 수강과목에서 선수과목의 구조를 예를 들 수 있다. 선수과목이 있다면 선수과목을 먼저 수강해야 하기 때문에 특정 과목을 수강해야 할 때 위상정렬을 이용할 수 있다.
[위상정렬 예시 - 선수과목을 고려한 학습순서]
위 그림에서 세 과목을 모두 듣기 위한 적절한 학습순서는?
데이터베이스 -> 데이터베이스 프로그래밍 -> 데이터베이스 프로그래밍 활용
[1 -> 2 -> 5 -> 3 -> 6 -> 4 -> 7]
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int vertex = sc.nextInt(); // 노드 개수
int edge = sc.nextInt(); // 간선의 개수
int[] indegree = new int[vertex+1];
// 모든 노드에 대한 진입차수는 0으로 초기화
List<List<Integer>> graph = new ArrayList<>();
// 각 노드에 연결된 간선 정보를 담기 위한 연결 리스트 초기화
// 그래프 초기화
for (int i = 0; i <= v; i++) {
graph.add(new ArrayList<Integer>());
}
// 방향 그래프의 모든 간선 정보를 입력 받기
for (int i = 0; i < e; i++) {
int a = sc.nextInt();
int b = sc.nextInt();
graph.get(a).add(b); // 정점 A에서 B로 이동 가능
indegree[b] += 1; // // 진입 차수를 1 증가
}
topologySort(vertex, edge, indegree, graph);
}
// 위상 정렬 함수
public static void topologySort(int vertex, int edge,
int[] indegree, List<List<Integer>> graph) {
List<Integer> result = new ArrayList<>();
// 알고리즘 수행 결과를 담을 리스트
Deque<Integer> queue = new ArrayDeque<>();
// 큐 라이브러리 사용
// 처음 시작할 때는 진입차수가 0인 노드를 큐에 삽입
for (int i = 1; i <= v; i++) {
if (indegree[i] == 0) {
queue.offer(i);
}
}
// 큐가 빌 때까지 반복
while (!queue.isEmpty()) {
// 큐에서 원소 꺼내기
int now = queue.poll();
result.add(now);
// 해당 원소와 연결된 노드들의 진입차수에서 1 빼기
for (int i = 0; i < graph.get(now).size(); i++) {
indegree[graph.get(now).get(i)] -= 1;
// 새롭게 진입차수가 0이 되는 노드를 큐에 삽입
if (indegree[graph.get(now).get(i)] == 0) {
queue.offer(graph.get(now).get(i));
}
}
}
// 위상 정렬을 수행한 결과 출력
for (int i = 0; i < result.size(); i++) {
System.out.print(result.get(i) + " ");
}
}
}
위상 정렬을 위해 차례대로 모든 노드를 확인하면서 각 노드에서 나가는 간선들을 하나씩 제거하는 과정을 거친다.
따라서, 위상정렬의 시간복잡도는 O(V + E)이다.
[Reference]
위상정렬
위상정렬을 Java로 구현해보자!!
위상정렬개념&문제풀이