Programming/알고리즘

위상 정렬(Topological Sort) 완벽 이해: 개념, 구현, 사이클 판별까지 한 번에 정리

octo54 2025. 4. 16. 13:29
반응형

위상 정렬(Topological Sort) 완벽 이해: 개념, 구현, 사이클 판별까지 한 번에 정리

**위상 정렬(Topological Sort)**은 방향 그래프(DAG: Directed Acyclic Graph)의 정점들을 선후 관계에 맞춰 순서대로 나열하는 알고리즘입니다.
선수 과목 → 후수 과목, 작업 순서 지정, 빌드 순서 등 순차적 의존성을 가지는 문제에서 자주 활용됩니다.

이번 글에서는 위상 정렬의 개념, 구현 방식(Kahn 알고리즘 / DFS 기반), 사이클 판별, 실전 예제까지 완벽하게 정리합니다.


✅ 위상 정렬(Topological Sort)이란?

방향성이 있는 그래프(DAG)에서,
"A → B"라면 A를 B보다 먼저 나열하는 방식으로 모든 정점을 정렬

🔹 위상 정렬의 전제 조건

  • 사이클이 없어야 한다 (Acyclic)
  • 반드시 **유향 그래프(Directed Graph)**여야 한다
  • 모든 노드를 정렬하지 못한다면 → 사이클 존재

🧠 실전 예시

  • 과목 이수 순서
  • 프로젝트 빌드 순서
  • 작업 스케줄링
  • 패키지 의존성 순서

📌 방법 1: Kahn's Algorithm (Queue 기반)

  1. 진입차수(in-degree)가 0인 노드를 큐에 삽입
  2. 큐에서 노드를 꺼내 정렬 리스트에 추가
  3. 해당 노드와 연결된 노드의 진입차수 1 감소
  4. 진입차수가 0이 되면 다시 큐에 삽입
  5. 모든 노드를 처리할 때까지 반복

🧪 Python 구현 (Kahn 알고리즘)

from collections import deque

def topological_sort(V, edges):
    graph = [[] for _ in range(V)]
    indegree = [0] * V

    # 그래프 및 진입차수 초기화
    for u, v in edges:
        graph[u].append(v)
        indegree[v] += 1

    queue = deque([i for i in range(V) if indegree[i] == 0])
    result = []

    while queue:
        node = queue.popleft()
        result.append(node)

        for neighbor in graph[node]:
            indegree[neighbor] -= 1
            if indegree[neighbor] == 0:
                queue.append(neighbor)

    if len(result) != V:
        return "사이클 존재 (위상 정렬 불가)"
    return result

edges = [(0, 2), (1, 2), (2, 3), (3, 4)]
print(topological_sort(5, edges))  # 출력: [0, 1, 2, 3, 4]

📌 방법 2: DFS 기반 위상 정렬

반응형
  • DFS 탐색 종료 후 역순으로 노드를 스택에 저장
def dfs_topo_sort(V, edges):
    graph = [[] for _ in range(V)]
    for u, v in edges:
        graph[u].append(v)

    visited = [False] * V
    result = []

    def dfs(node):
        visited[node] = True
        for neighbor in graph[node]:
            if not visited[neighbor]:
                dfs(neighbor)
        result.append(node)  # 후위 순회

    for i in range(V):
        if not visited[i]:
            dfs(i)

    return result[::-1]  # 역순 반환

edges = [(0, 2), (1, 2), (2, 3), (3, 4)]
print(dfs_topo_sort(5, edges))  # 출력: [1, 0, 2, 3, 4]

⚠️ 사이클 판별

  • Kahn 알고리즘: 결과 리스트의 길이 ≠ V → 사이클 존재
  • DFS 방식: 탐색 중 visited + recursion stack에 같은 노드 감지 → 사이클 존재
def has_cycle(V, edges):
    graph = [[] for _ in range(V)]
    for u, v in edges:
        graph[u].append(v)

    visited = [0] * V  # 0: 미방문, 1: 방문중, 2: 방문완료

    def dfs(node):
        if visited[node] == 1:
            return True  # 사이클 존재
        if visited[node] == 2:
            return False

        visited[node] = 1
        for neighbor in graph[node]:
            if dfs(neighbor):
                return True
        visited[node] = 2
        return False

    for i in range(V):
        if dfs(i):
            return True
    return False

🎯 실전 활용 문제 유형

문제 유형 설명

과목 순서 정하기 선수 과목이 있는 과목들을 올바른 순서로 나열
작업 우선순위 이전 작업이 끝나야 시작할 수 있는 작업 나열
빌드 순서 라이브러리 또는 패키지 의존성 해결 순서
DAG의 사이클 판별 위상 정렬 불가능 시 → 순환 의존 확인

📌 결론 및 요약

  • 위상 정렬은 DAG에서 선행 관계를 가진 작업을 순서대로 나열할 때 사용
  • Kahn 알고리즘은 진입차수 기반, DFS 방식은 후위 탐색 기반
  • 사이클이 존재하면 위상 정렬 불가, 이를 판별하는 데도 활용 가능

👉 다음 글에서는 유니온 파인드(Union-Find) 자료구조를 기반으로
사이클 판별, 네트워크 그룹화, 최소 신장 트리 연결 등 실전 응용을 알아봅니다.


📚 참고자료 및 출처

  • 『이것이 취업을 위한 코딩 테스트다 with Python』
  • 백준 2252(줄 세우기), 1516(게임 개발), 2637(장난감 조립)
  • GeeksforGeeks – Topological Sort

 

위상정렬, topological sort, kahn algorithm, dfs 위상정렬, 사이클 탐지, DAG, 작업순서, 파이썬 그래프, 코딩테스트 준비, SEO 최적화 10개