참고: 위키백과
위상 정렬(topological sorting)은 directed graph(방향이 있는 그래프)의 노드(vertex)의 선행 순서를 거스르지 않으면서 나열하는 것을 의미한다.
topological sort를 가장 잘 설명해 줄 수 있는 예로 대학의 선수과목(prerequisite) 구조를 예로 들 수 있다. 만약 특정 수강과목에 선수과목이 있다면 그 선수 과목부터 수강해야 하므로, 특정 과목들을 수강해야 할 때 위상 정렬을 통해 올바른 수강 순서를 찾아낼 수 있다.
이와 같이 선후 관계가 정의된 그래프 구조 상에서 선후 관계에 따라 정렬하기 위해 위상 정렬을 이용할 수 있다.
정렬의 순서는 그래프의 구조에 따라 여러 개의 종류가 나올 수 있다. topological sort이 성립하기 위해서는 반드시 그래프의 순환이 존재하지 않아야 한다. 즉, 그래프가 DAG(directed acyclic graph)여야 한다.
인접 리스트를 사용할 경우: O(V+E)
이걸 사용하면 leetcode의 207. Course Schedule, 210. Course Schedule II 문제를 아주 쉽게 풀 수 있다. 그 중 210번. Course Schedule II를 풀어본다.
문제 설명을 보면 pre[i] = [ai, bi] 의 의미는 bi 강의를 듣고 ai를 들을 수 있다고 나와있다. 방향 그래프로 표현하면 bi -> ai 이 되겠다.
prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai.
class Solution {
public:
vector<int> v[2000];
vector<int> findOrder(int num, vector<vector<int>>& pre) {
unordered_map<int, int> indegree;
for (int i = 0; i < pre.size(); i++) {
indegree[pre[i][0]]++;
v[pre[i][1]].push_back(pre[i][0]);
}
queue<int> q;
for (int i = 0; i < num; i++) {
if (indegree[i] == 0) {
q.push(i);
}
}
vector<int> ans;
while (!q.empty()) {
int val = q.front();
q.pop();
ans.push_back(val);
for (int i = 0; i < v[val].size(); i++) {
indegree[v[val][i]]--;
if (indegree[v[val][i]] == 0) {
q.push(v[val][i]);
}
}
}
if (ans.size() != num) {
ans.clear();
}
return ans;
}
};
순환이 존재하는지 알수 있는 이유?
순환이 존재하면 in-degree가 0이 될 수 없어 전체 노드를 탐색할 수 없다.