[그래프/정렬/유향] 위상정렬 / Topological Sorting
2024. 10. 20. 13:26ㆍ🐣/Algorithm
위상정렬 / Topological Sorting
유향 비순환 그래프(Directed Acyclic Graph, DAG) 에서 각 노드들을 선형 순서로 나열하는 방법입니다. 여기서 유향 비순환 그래프란, 방향이 있는 그래프 중에 사이클이 없는 그래프를 말합니다. 위상 정렬은 보통 의존 관계를 해결할 때 사용되며, 그래프 내에서 어떤 일의 순서를 정하거나 작업 간의 선후 관계를 정의하는 문제를 풀 때 유용합니다.
위상 정렬의 조건
그래프는 반드시 유향 비순환 그래프(DAG)이어야 합니다.
- 유향(Directed): 간선의 방향이 있다.
- 비순환(Acyclic): 사이클이 존재하지 않는다. 즉, 위상 정렬은 순환하는 경로가 있으면 불가능합니다.
위상 정렬의 동작 원리
위상 정렬의 핵심은 선행 조건을 만족하는 노드부터 순차적으로 처리하는 것입니다. 즉, 어떤 노드를 처리하려면 그 노드에 들어오는 모든 간선을 먼저 제거해야 합니다. 이를 통해 의존성이 있는 작업들이 먼저 처리되며, 나중에 의존하는 작업들을 차례로 수행할 수 있습니다.
위상 정렬의 활용
위상 정렬은 다음과 같은 다양한 문제들에 유용하게 사용됩니다.
- 작업 스케줄링: 작업 간의 의존 관계가 있는 경우, 어떤 작업을 먼저 수행해야 할지 결정할 때 사용됩니다.
- 컴파일러: 프로그램의 컴파일 순서를 정할 때, 각 모듈 간의 의존 관계를 위상 정렬로 해결할 수 있습니다.
- 과목 수강 순서: 선수 과목이 있는 학과에서 수강 순서를 결정할 때 활용됩니다.
- 프로젝트 관리: 프로젝트 내 여러 태스크의 의존성을 해결하는 데 사용될 수 있습니다.
위상 정렬의 대표적인 알고리즘
- Kahn's Algorithm (차수 기반 접근)
- DFS(Depth First Search)를 활용한 방법
![]() |
왼쪽의 그래프는 많은 유용한 위상 정렬을 보여준다.
|
코드 보기
Kahn's Algorithm을 사용한 Swift code
import Foundation struct Queue<T> { var index: Int = 0 var queue: [T] = [] public var isEmpty: Bool { return queue.count == index } mutating func push(_ e: T) { queue.append(e) } mutating func pop() -> T? { if !self.isEmpty { let element = queue[index] index += 1 return element } return nil } } func topologicalSort(n: Int, m: Int, edges: [(Int, Int)]) { var graph = Array(repeating: [Int](), count: n + 1) var inDegree = Array(repeating: 0, count: n + 1) for (x, y) in edges { graph[x].append(y) inDegree[y] += 1 } var q = Queue<Int>() for i in 1...n { if inDegree[i] == 0 { q.push(i) } } var result: [Int] = [] while !q.isEmpty { if let current = q.pop() { result.append(current) for next in graph[current] { inDegree[next] -= 1 if inDegree[next] == 0 { q.push(next) } } } } for student in result { print(student, terminator: " ") } print() } let nm = readLine()!.split(separator: " ").map{Int(String($0))!} let (n, m) = (nm[0], nm[1]) var edges: [(Int, Int)] = [] for _ in 0..<m { let edge = readLine()!.split(separator: " ").map{Int(String($0))!} edges.append((edge[0], edge[1])) } topologicalSort(n: n, m: m, edges: edges)
코드 보기
DP와 위상정렬을 사용한 c++ code
#include <algorithm> #include <iostream> #include <queue> #include <vector> using namespace std; int main() { int n; cin >> n; vector<vector<int>> graph(n + 1); vector<int> in_degree(n + 1, 0); vector<int> build_time(n + 1, 0); vector<int> total_time(n + 1, 0); for (int i = 1; i <= n; i++) { cin >> build_time[i]; int input; while (true) { cin >> input; if (input == -1) break; graph[input].push_back(i); in_degree[i]++; } } queue<int> q; for (int i = 1; i <= n; i++) { if (in_degree[i] == 0) { q.push(i); total_time[i] = build_time[i]; } } while (!q.empty()) { int current = q.front(); q.pop(); for (int next : graph[current]) { in_degree[next]--; total_time[next] = max(total_time[next], total_time[current] + build_time[next]); if (in_degree[next] == 0) { q.push(next); } } } for (int i = 1; i <= n; i++) { cout << total_time[i] << "\n"; } return 0; }
출처
위상정렬 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. 위상 정렬(topological sorting)은 유향 그래프의 꼭짓점들(vertex)을 변의 방향을 거스르지 않도록 나열하는 것을 의미한다. 위상정렬을 가장 잘 설명해 줄 수 있는 예
ko.wikipedia.org
'🐣 > Algorithm' 카테고리의 다른 글
[그래프] 다익스트라 / Dijkstra's algorithm (0) | 2024.10.03 |
---|---|
[누적합] 세그먼트트리 / Segment tree (0) | 2024.10.02 |
[그래프] 벨만-포드 / Bellman–Ford algorithm (2) | 2024.10.01 |
[Swift] 순열 / permutation / 조합 / combination (0) | 2024.04.21 |
[DP] 타뷸레이션 (0) | 2024.03.21 |