Computer Science/Algorithm

[Algorithm] 위상 정렬(Topological sort) 알고리즘

great_park 2023. 1. 28. 15:03
반응형

Topological sort

위상 정렬이란, 사이클이 없는 방향 그래프(DAG)를 방향성에 거스르지 않도록 순서대로 나열하는 것입니다.
대표적인 예시로, 선수 과목을 고려해서 학습 순서를 설정하는 것이 있습니다. 
일반화학, 유기화학1, 유기화학2 라는 3과목이 있을 때, 아래의 학습 순서들 중 적절한 순서는 (2)가 될 것입니다.
(1) 일반화학 -> 유기화학2 -> 유기화학1
(2) 일반화학 -> 유기화학1 -> 유기화학2

우선, 비순환 방향 그래프(DAG)에 대해 정리하겠습니다.

비순환 방향 그래프 (Directed Acyclic Graph)


위와 같이 방향 그래프이면서 사이클이 없는 그래프를 DAG라고 한다.

DAG와 Topological sort
위상 정렬은 이러한 비순환 방향 그래프에서 정점들을 선형으로 정렬하는 것이다.
모든 간선 (u,v)에 대해 정점 u가 정점 v보다 먼저 오는 순서로 정렬되는 것이다,
이때, 위상 정렬의 결과는 반드시 하나가 아닐 수도 있다. 이는 후에 설명하도록 하겠다.

정렬 결과를 그림으로 표현하면 아래와 같다.

진입차수와 진출차수
진입차수: 특정한 정점으로 들어오는 간선의 개수
진출차수: 특정한 정점에서 나가는 간선의 개수

 

위상 정렬 알고리즘

과정을 살펴보면 아래와 같습니다.

  1. 진입차수가 0인 모든 노드를 큐에 넣는다.
  2. 큐가 빌 때까지 다음을 반복한다.
    1. 큐에서 원소를 꺼내 해당 노드에서 나가는 간선을 그래프에서 제거한다.
    2. 새롭게 진입차수가 0이 된 노드를 큐에 넣는다.

아래의 DAG을 위상 정렬하는 예시를 보이겠습니다.

  • [초기 단계] 초기 단계에서는 진입차수가 0인 모든 노드 큐에 넣는다
    • 처음에 노드 1이 큐에 삽입된다

  • [Step 1] 큐에서 노드 1을 꺼낸 뒤에 노드 1에서 나가는 간선을 제거한다
    • 새롭게 진입차수가 0이 된 노드들을 큐에 삽입한다

  • [Step 2] 큐에서 노드 2를 꺼낸 뒤에 노드 2에서 나가는 간선을 제거한다
    • 새롭게 진입차수가 0이 된 노드들을 큐에 삽입한다

  • [Step 3] 큐에서 노드 5를 꺼낸 뒤에 노드 5에서 나가는 간선을 제거한다
    • 새롭게 진입차수가 0이 된 노드들을 큐에 삽입한다

  • [Step 4] 큐에서 노드 3를 꺼낸 뒤에 노드 3에서 나가는 간선을 제거한다
    • 새롭게 진입차수가 0이 된 노드들을 큐에 삽입한다

  • [Step 5] 큐에서 노드 6을 꺼낸 뒤에 노드 6에서 나가는 간선을 제거한다
    • 새롭게 진입차수가 0이 된 노드들을 큐에 삽입한다

  • [Step 6] 큐에서 노드 4를 꺼낸 뒤에 노드 4에서 나가는 간선을 제거한다
    • 새롭게 진입차수가 0이 된 노드들을 큐에 삽입한다

  • [Step 7] 큐에서 노드 7을 꺼낸 뒤에 노드 7에서 나가는 간선을 제거한다
    • 새롭게 진입차수가 0이 된 노드들을 큐에 삽입한다

  • [위상 정렬 결과]
    • 큐에 삽입된 전체 노드 순서: 1 → 2 → 5 → 3 → 6 → 4 → 7

 

 

Code

from collections import deque

# 노드의 개수와 간선의 개수를 입력 받기
v, e = map(int, input().split())
# 모든 노드에 대한 진입차수는 0으로 초기화
indegree = [0] * (v + 1)
# 각 노드에 연결된 간선 정보를 담기 위한 연결 리스트 초기화
graph = [[] for i in range(v + 1)]

# 방향 그래프의 모든 간선 정보를 입력 받기
for _ in range(e):
    a, b = map(int, input().split())
    graph[a].append(b) # 정점 A에서 B로 이동 가능
    # 진입 차수를 1 증가
    indegree[b] += 1

# 위상 정렬 함수
def topology_sort():
    result = [] # 알고리즘 수행 결과를 담을 리스트
    q = deque() # 큐 기능을 위한 deque 라이브러리 사용

    # 처음 시작할 때는 진입차수가 0인 노드를 큐에 삽입
    for i in range(1, v + 1):
        if indegree[i] == 0:
            q.append(i)

    # 큐가 빌 때까지 반복
    while q:
        # 큐에서 원소 꺼내기
        now = q.popleft()
        result.append(now)
        # 해당 원소와 연결된 노드들의 진입차수에서 1 빼기
        for i in graph[now]:
            indegree[i] -= 1
            # 새롭게 진입차수가 0이 되는 노드를 큐에 삽입
            if indegree[i] == 0:
                q.append(i)

    # 위상 정렬을 수행한 결과 출력
    for i in result:
        print(i, end=' ')

topology_sort()

 

 

reference

https://freedeveloper.tistory.com/390

반응형