어떤 문제를 푸는가
작업들 사이에 "A를 끝내야 B를 시작할 수 있다" 같은 의존 관계 가 있을 때,
모든 의존을 어기지 않는 하나의 직선 순서 를 찾는 것이 위상 정렬입니다.
선수 과목 수강 순서, 빌드 의존성, 컴파일 순서 등이 대표 예입니다.
의존 관계를 방향 간선 \(u \to v\) ("u가 v보다 먼저")로 모으면 그래프가 됩니다.
위상 정렬이 가능하려면 이 그래프가 사이클이 없는 방향 그래프(DAG) 여야
합니다. 사이클이 있으면 순서를 정할 수 없습니다(서로 먼저여야 함).
방법 1 — 진입 차수 기반 (칸 알고리즘)
각 정점의 진입 차수(들어오는 간선 수)를 셉니다.
- 진입 차수 0인 정점들을 큐에 넣는다 (선행 작업이 없는 것들).
- 큐에서 하나 꺼내 결과에 추가하고, 그 정점에서 나가는 간선을 "제거"하며
이웃의 진입 차수를 1씩 줄인다. - 새로 진입 차수가 0이 된 정점을 큐에 넣는다.
- 큐가 빌 때까지 반복.
정당성: 진입 차수 0인 정점은 어떤 선행 조건도 없으므로 지금 처리해도
안전합니다. 그 정점을 빼면 다른 정점들의 선행 조건이 하나씩 줄고, 그렇게
의존이 하나씩 풀려 나갑니다.
방법 2 — DFS 후위 순서 뒤집기
각 정점에서 DFS를 돌고, 함수가 끝나는(자식을 다 본) 순서 로 스택에 쌓은
뒤 뒤집습니다. 자식이 모두 끝난 뒤 부모가 쌓이므로, 뒤집으면 부모가 앞섭니다.
사이클 탐지
칸 알고리즘에서 결과에 들어간 정점 수가 \(V\)보다 작으면 사이클이 있습니다.
사이클 위의 정점들은 진입 차수가 끝내 0이 되지 못해 큐에 못 들어가기 때문입니다.
복잡도
| 항목 | 값 |
|---|---|
| 시간 | \(O(V + E)\) |
| 공간 | \(O(V + E)\) |
모든 간선과 정점을 상수 번 보므로 선형입니다.
답이 여러 개일 수 있다
진입 차수 0인 정점이 동시에 여러 개면 어느 것을 먼저 빼도 유효합니다. 따라서
위상 순서는 유일하지 않을 수 있습니다. "사전순으로 가장 빠른 순서" 같은
조건이 붙으면 큐 대신 우선순위 큐 를 씁니다. 다음 강의에서 구현과 응용을
봅니다.