728x90
위상정렬 (Topological Sorting)
1. 개요
위상정렬(Topological Sorting)은 방향성 비순환 그래프(DAG, Directed Acyclic Graph)에서 노드들을 선형으로 정렬하는 방법이다.
이 정렬은 그래프의 모든 간선 (u, v)
에 대해 노드 u
가 노드 v
보다 앞에 오도록 정렬한다.
위상정렬은 순서가 필요한 작업 스케줄링, 컴파일러의 의존성 분석, 데이터 처리 파이프라인 등 다양한 분야에서 활용된다.
2. 알고리즘의 원리
위상정렬은 두 가지 주요 알고리즘을 사용한다:
- Kahn의 알고리즘 (BFS 기반): 진입 차수(indegree)를 기반으로 정렬한다.
- 깊이 우선 탐색 (DFS 기반): 각 노드를 방문한 후, 역순으로 정렬한다.
위상정렬이 가능한 그래프는 사이클이 없는 방향 그래프 (DAG)여야 한다. 만약 사이클이 존재한다면 위상정렬을 수행할 수 없다.
3. 위상정렬의 수행과정
- 진입 차수가 0인 노드를 찾는다.
- 이 노드를 결과 리스트에 추가하고, 해당 노드에서 나가는 간선을 제거한다.
- 새로운 진입 차수가 0인 노드를 찾고, 2번 과정을 반복한다.
- 모든 노드를 방문할 때까지 이 과정을 반복한다.
그래프의 모든 노드를 방문하기 전에 진입 차수가 0인 노드가 없다면 사이클이 존재하는 것!
4. 위상정렬의 유형
4-1. Kahn의 알고리즘 (BFS 기반)
Kahn의 알고리즘은 진입 차수가 0인 노드를 큐에 넣고, BFS 방식으로 노드를 처리하면서 위상정렬을 수행한다.
예시 문제
문제: 다음 그래프를 위상정렬하기
1 → 2 → 4
1 → 3 → 4
예시 코드
from collections import deque
# 그래프 정의
n = 4
graph = {1: [2, 3], 2: [4], 3: [4], 4: []}
indegree = [0] * (n + 1)
# 진입 차수 계산
for nodes in graph.values():
for node in nodes:
indegree[node] += 1
# 진입 차수가 0인 노드를 큐에 삽입
queue = deque([i for i in range(1, n + 1) if indegree[i] == 0])
result = []
# Kahn's 알고리즘 수행
while queue:
current = queue.popleft()
result.append(current)
for neighbor in graph[current]:
indegree[neighbor] -= 1
if indegree[neighbor] == 0:
queue.append(neighbor)
print("위상정렬 결과:", result)
출력
위상정렬 결과: [1, 2, 3, 4]
4-2. 깊이 우선 탐색 (DFS 기반)
DFS 기반의 위상정렬은 노드를 방문한 후, 재귀 호출이 끝날 때 해당 노드를 스택에 넣어 정렬을 수행한다.
예시 문제
문제: 다음 그래프를 위상정렬하기
5 → 2
5 → 0
4 → 0
4 → 1
2 → 3
3 → 1
예시 코드
from collections import defaultdict
# 그래프 정의
graph = defaultdict(list)
graph[5].extend([2, 0])
graph[4].extend([0, 1])
graph[2].append(3)
graph[3].append(1)
visited = set()
stack = []
def dfs(v):
visited.add(v)
for neighbor in graph[v]:
if neighbor not in visited:
dfs(neighbor)
stack.append(v)
for node in range(6):
if node not in visited:
dfs(node)
print("위상정렬 결과:", stack[::-1])
출력
위상정렬 결과: [5, 4, 2, 3, 1, 0]
4-3. Parallel 알고리즘
Parallel 위상정렬은 여러 노드를 동시에 처리하는 알고리즘으로, 주로 병렬 컴퓨팅 환경에서 사용된다.
이 방법은 Kahn의 알고리즘을 확장하여 여러 작업을 병렬로 처리한다.
특징
- 여러 진입 차수가 0인 노드를 동시에 처리한다.
- 병렬 처리를 통해 수행 시간을 줄일 수 있다.
이 알고리즘은 대규모 작업 스케줄링이나 병렬 컴파일러에서 사용된다.
5. 최단 거리 찾기에서 적용
위상정렬은 DAG에서 최단 거리 문제를 해결하는 데 사용될 수 있다.
과정
- 그래프를 위상정렬한다.
- 시작 노드로부터의 거리를 기록한다.
- 위상정렬 순서대로 각 노드를 확인하며, 인접 노드로의 거리를 갱신한다.
예시 코드
from collections import deque
# 그래프 정의
n = 6
graph = {0: [(1, 5), (2, 3)], 1: [(3, 6)], 2: [(3, 7), (4, 4)], 3: [(5, 2)], 4: [(5, 1)], 5: []}
indegree = [0] * n
# 진입 차수 계산
for u in graph:
for v, _ in graph[u]:
indegree[v] += 1
# 위상정렬 수행
queue = deque([i for i in range(n) if indegree[i] == 0])
distance = [0] * n
while queue:
current = queue.popleft()
for neighbor, weight in graph[current]:
indegree[neighbor] -= 1
distance[neighbor] = max(distance[neighbor], distance[current] + weight)
if indegree[neighbor] == 0:
queue.append(neighbor)
print("최단 거리:", distance)
출력
최단 거리: [0, 5, 3, 12, 7, 14]
6. 특이한 점
- 사이클 감지: 위상정렬 중 진입 차수가 0인 노드가 없으면 사이클이 존재한다.
- 유일한 정렬이 아닐 수 있음: DAG에 대해 위상정렬 결과는 유일하지 않을 수 있다.
반응형
'알고리즘 > 알고리즘 정리' 카테고리의 다른 글
알고리즘 유형공부 - 연결 리스트 (Linked List) (1) | 2024.12.23 |
---|---|
알고리즘 유형공부 - 이분탐색(Binary Search)과 투포인터(Two Pointer) (1) | 2024.12.20 |
알고리즘 유형공부 - 비트마스킹 (BitMasking) (0) | 2024.12.18 |
알고리즘 유형공부 - BFS와 DFS (0) | 2024.12.17 |