위상정렬이란, 방향 그래프의 모든 노드를 '방향성에 거스르지 않도록 순서대로 나열하는 것'이다.
키 포인트는 '진입차수(indegree)'이다.
1) 진입차수가 0인 노드를 큐에 넣는다.
2) 큐가 빌 때까지 다음의 과정을 반복한다.
2-1) 큐에서 원소를 꺼내 해당 노드에서 출발하는 간선을 그래프에서 제거한다.
2-2) 새롭게 진입차수가 0이 된 노드를 큐에 넣는다.
위상정렬의 시간복잡도는 O(V+E)이다.
차례대로 모든 노드를 확인하면서 해당노드에서 출발하는 간선을 제거한다.
즉, 노드와 간선을 전부 한번씩 탐색한다고 생각하면 된다.
큐에 I/O하는 시간은 V와 같으므로 무시한다.
주의사항은 다음과 같다.
1. 위상정렬 도중, 사이클이 발생할 수도 있다.
2. 위상정렬로 나열한 경우의 수가 2가지 이상일 수 있다.
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// 노드의 개수(v)와 간선의 개수(e)
// 노드의 개수는 최대 100000개 라고 가정
int v, e;
// 모든 노드에 대한 진입차수는 0으로 초기화
int indegree[100001];
// 각 노드에 연결된 간선 정보를 담기위한 인접 리스트 초기화
vector<int> graph[100001];
// 위상 정렬 함수
void topologySort() {
vector<int> result; // 알고리즘 수행 결과를 담을 리스트
queue<int> q; // 진입차수 0인 노드를 넣을 큐
// 처음 시작할 때는 진입차수 0인 노드를 큐에 삽입
for (int i = 1; i <= v; i++) {
if (indegree[i] == 0) {
q.push(i);
}
}
// 큐가 빌때까지 반복
while (!q.empty()) {
// 큐에서 원소 꺼내기
int now = q.front();
q.pop();
result.push_back(now);
// 해당 원소와 연결된 노드들의 진입차수에서 1 빼기
for (int e : graph[now]) {
indegree[e]--;
// 새롭게 진입차수가 0이 되는 노드 큐에 삽입
if (indegree[e] == 0) {
q.push(e);
}
}
}
// 위상 정렬을 수행한 결과 출력
for (int x: result) {
cout << x << " ";
}
cout << endl;
}
int main(void) {
cin >> v >> e;
// 방향그래프의 모든 간선 정보를 입력 받기
for (int i = 0; i < e; i++) {
int a, b;
cin >> a >> b;
graph[a].push_back(b); // 정점 a에서 b로 이동 가능
// 진입 차수를 1 증가
indegree[b] += 1;
}
topologySort();
return 0 ;
}
'알고리즘 > Template' 카테고리의 다른 글
[Algorithm][Template] 2차원 리스트 90도 돌리기 (0) | 2022.01.20 |
---|---|
[Algorithm][Template] 소수의 판별 (0) | 2022.01.20 |
[Algorithm][Template] 크루스칼 알고리즘 (0) | 2022.01.03 |
[Algorithm][Template] 서로소 집합(Disjoint Set), Union-Find (0) | 2022.01.02 |
[Algorithm][Template] 플로이드 워셜 알고리즘 (0) | 2021.12.29 |