위상정렬이란, 방향 그래프의 모든 노드를 '방향성에 거스르지 않도록 순서대로 나열하는 것'이다.

 

키 포인트는 '진입차수(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 ;
}

 

+ Recent posts