자료구조
자료구조 - 위상 정렬
tita
2024. 6. 11. 13:29
[위상 정렬(Topological Sort)]
방향성 비순환 그래프(DAG: Directed Acyclic Graph)의 정점을 순서대로 나열하는 알고리즘입니다.
상 정렬은 그래프의 각 간선 (u, v)에 대해 정점 u가 v보다 먼저 나오는 순서를 찾습니다.
위상 정렬은 DFS 기반의 방법과 Kahn's Algorithm 방법이 있습니다.
위상 정렬의 의사코드(Kahn's Algorithm)
- 초기화:
- 각 정점의 진입 차수를 계산하여 배열에 저장합니다.
- 진입 차수가 0인 모든 정점을 큐에 넣습니다.
- 큐 처리:
- 큐에서 정점을 하나씩 꺼내서 결과 리스트에 추가합니다.
- 꺼낸 정점의 모든 인접 정점의 진입 차수를 1씩 감소시킵니다.
- 감소된 진입 차수가 0이 된 정점을 큐에 추가합니다.
- 사이클 검출:
- 결과 리스트의 길이가 정점의 총 개수와 같지 않으면 사이클이 존재한다고 판단합다.
함수 topologicalSort(graph):
in_degree = [0] * 정점의 수(graph)
for 각 정점 u in graph:
for 각 정점 v in 인접 리스트(u):
in_degree[v] += 1 // 진입 차수 계산
queue = []
for 각 정점 u in graph:
if in_degree[u] == 0:
queue에 u 추가 // 초기 큐 설정
top_order = []
while queue가 비어있지 않으면:
u = queue에서 꺼냄
top_order에 u 추가
for 각 정점 v in 인접 리스트(u):
in_degree[v] -= 1 // 진입 차수 감소
if in_degree[v] == 0:
queue에 v 추가
if top_order의 길이 != 정점의 수(graph):
오류 "그래프에 사이클이 존재합니다."
반환 top_order
위상 정렬의 예시 코드(Khan's Algorithm)
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// 위상 정렬 함수를 정의
vector<int> topologicalSort(int V, vector<vector<int>>& adj)
{
vector<int> in_degree(V, 0); // 각 정점의 진입 차수를 저장할 벡터
// 각 정점의 진입 차수를 계산
for (int u = 0; u < V; u++)
{
for (int v : adj[u])
{
in_degree[v]++; // 인접 리스트를 통해 각 정점의 진입 차수를 증가
}
}
queue<int> q;
// 진입 차수가 0인 모든 정점을 큐에 삽입
for (int i = 0; i < V; i++)
{
if (in_degree[i] == 0)
{
q.push(i);
}
}
vector<int> top_order;
// 큐에서 정점을 하나씩 꺼내어 결과 리스트에 추가
while (!q.empty())
{
int u = q.front();
q.pop();
top_order.push_back(u);
// 해당 정점의 인접한 정점들의 진입 차수를 감소시키고, 새로 진입 차수가 0이 된 정점을 큐에 삽입
for (int v : adj[u])
{
if (--in_degree[v] == 0)
{
q.push(v);
}
}
}
// 만약 정렬된 정점의 수가 V와 다르면 사이클이 존재
if (top_order.size() != V)
{
throw runtime_error("그래프에 사이클이 존재합니다.");
}
return top_order; // 위상 정렬된 결과 반환
}
int main()
{
int V = 6; // 정점의 수
vector<vector<int>> adj(V);
// 간선 추가 (예시: 유방향 그래프)
adj[5].push_back(2);
adj[5].push_back(0);
adj[4].push_back(0);
adj[4].push_back(1);
adj[2].push_back(3);
adj[3].push_back(1);
try
{
vector<int> result = topologicalSort(V, adj);
cout << "위상 정렬 결과: ";
for (int v : result)
{
cout << v << " ";
}
cout << endl;
}
catch (const runtime_error& e)
{
cout << e.what() << endl;
}
return 0;
}
위상 정렬의 의사코드(DFS)
- 초기화:
- 모든 정점을 방문하지 않은 상태로 표시합니다.
- 빈 스택을 하나 준비합니다.
- DFS 수행:
- 방문하지 않은 정점에서 DFS를 시작합니다.
- DFS 방문 중, 방문을 완료한 정점을 스택에 삽입합니다.
- 결과 출력:
- 스택에 저장된 정점들을 꺼내면 위상 정렬의 결과가 됩니다.
function topologicalSort(graph):
visited = [false] * number_of_vertices(graph)
stack = []
function dfs(u):
visited[u] = true
for each vertex v in neighbors(u):
if not visited[v]:
dfs(v)
push(stack, u)
for each vertex u in graph:
if not visited[u]:
dfs(u)
top_order = []
while not is_empty(stack):
append(top_order, pop(stack))
return top_order
위상 정렬의 예시 코드(DFS)
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
void dfs(int u, vector<vector<int>>& adj, vector<bool>& visited, stack<int>& Stack)
{
visited[u] = true; // 현재 정점을 방문 표시
// 인접한 모든 정점을 탐색
for (int v : adj[u])
{
if (!visited[v])
{
dfs(v, adj, visited, Stack); // 방문하지 않은 정점에 대해 DFS 수행
}
}
Stack.push(u); // 모든 인접 정점을 방문한 후에 스택에 현재 정점을 삽입
}
// 위상 정렬 함수
vector<int> topologicalSort(int V, vector<vector<int>>& adj)
{
vector<bool> visited(V, false); // 모든 정점을 방문하지 않은 상태로 초기화
stack<int> Stack; // 정점을 저장할 스택
// 모든 정점에 대해 DFS 수행
for (int i = 0; i < V; i++)
{
if (!visited[i])
{
dfs(i, adj, visited, Stack);
}
}
vector<int> top_order;
// 스택에서 정점을 꺼내어 위상 정렬 순서로 저장
while (!Stack.empty())
{
top_order.push_back(Stack.top());
Stack.pop();
}
return top_order; // 위상 정렬된 결과 반환
}
int main()
{
int V = 6; // 정점의 수
vector<vector<int>> adj(V);
// 간선 추가 (예시: 유방향 그래프)
adj[5].push_back(2);
adj[5].push_back(0);
adj[4].push_back(0);
adj[4].push_back(1);
adj[2].push_back(3);
adj[3].push_back(1);
vector<int> result = topologicalSort(V, adj);
cout << "위상 정렬 결과: ";
for (int v : result)
{
cout << v << " ";
}
cout << endl;
return 0;
}