728x90
DFS의 장점 중 하나는 각 노드를 순회하면서 다양한 정보를 남길 수 있다는 것이다. 이를 활용해서 그래프의 사이클이 생겼을 때 이를 감지하는 기능을 구현할 수 있다. 이 기능은 DeadLock 즉, 교착상태를 확인할 때 매우 유용하다. DeadLock에 대한 설명은 생략하고 이 사이클을 판별하는 조건에 대해서 알아보도록 하자.
사이클이 생긴다고 하는 상황을 판별하기 위해서는 총 3가지 단계를 거쳐야 한다.
- 순방향 간선인가? ( 이 경우 사이클이 생길 수 없음 )
- 순방향 간선이 아니라면 교차 간선인가? ( 교차 간선일 경우 사이클이 아님 )
- 그렇다면 역방향 간선이고 이 경우 무조건 사이클이 생겼다고 할 수 있다.

순방향 간선이란 위 그림과 같이 한 방향으로 쭉 흘러가는 경우 이다. 0을 기준으로 순회 하면 0 -> 1 -> 3 -> 2가 될거고 이 경우 사이클이 생길 수 없다.

교차 간선이란 이미 특정 노드를 기준으로 탐색이 완료되었고, 다른 노드를 기준으로 순회를 시작했을 때 방문한 곳을 탐색한 경우이다. 즉 이미 0번을 기준으로 순회 하여 2번은 탐색이 된 상황인데 4번을 기준으로 한번 더 순회를 했을 때 2번으로 들어오게 된 경우가 교차 간선에 해당한다.

이제 다음과 같은 그림처럼 처음 0번을 기준으로 순회한다고 했을 때 순방향, 교차 간선이 아니니 역방향 간선에 해당하여 사이클이 발생했다고 판단할 수 있다. DFS에서 해당 조건만 추가해주면 사이클을 판별할 수 있다.
#include <iostream>
using namespace std;
#define endl "\n"
#include <map>
#include <unordered_map>
#include <set>
#include <vector>
vector<vector<int>> _vec;
map<int, set<int>> _history;
vector<int> _visitedOrder;
int _visitedCount;
vector<bool> _finished;
vector<int> _parent;
void DFS(int here)
{
// 이미 방문한 곳이면 pass
if (_visitedOrder[here] != -1)
return;
// 몇 번째로 방문한 곳인지를 기입
cout << here << "는(은) " << _visitedCount + 1 << "번째" << endl;
_visitedOrder[here] = _visitedCount++;
auto findIt = _history.find(here);
if (findIt == _history.end())
{
// 연결된 노드들이 없다.
_finished[here] = true;
return;
}
// 연결된 노드가 있다.
set<int>& next = findIt->second;
for (int there : next)
{
if (_visitedOrder[there] == -1)
{
// 연결된 노드를 아직 방문한 적이 없다
// 연결된 노드의 부모를 현재 위치로 설정 (추적용)
_parent[there] = here;
DFS(there);
continue;
}
// 순방향 간선인 상황 (방문 순번으로 판단)
if (_visitedOrder[here] < _visitedOrder[there])
continue;
// 역방향 간선
if (_finished[there] == false)
{
// DFS가 아직 끝나지 않은 상황
// 만약 _finished[there]이 true라면 교차간선임
cout << "Cycle Detected!!" << endl;
cout << here << " -> " << there << endl;
int now = here;
while (now != there)
{
// 부모를 추적해서 어떻게 사이클이 형성되었는지 출력
cout << _parent[now] << " -> " << now << endl;
now = _parent[now];
}
}
}
_finished[here] = true;
}
void Push(int node, int edge)
{
_vec[node].push_back(edge);
set<int>& history = _history[node];
history.insert(edge);
}
int main()
{
cin.tie(NULL);
ios::sync_with_stdio(false);
_visitedOrder = vector<int>(5, -1);
_visitedCount = 0;
_finished = vector<bool>(5, false);
_parent = vector<int>(5, -1);
_vec.resize(5);
Push(0, 1);
Push(0, 2);
Push(1, 3);
Push(3, 0);
Push(4, 2);
DFS(0);
}

데이터는 위의 그림과 같이 그래프가 형성되도록 하였고, 0번 기준으로 순회를 했을 때 사이클을 잘 발견한다는 것을 확인할 수 있다.
728x90
'C++ > 자료구조' 카테고리의 다른 글
BFS(Breadth First Search) 너비 우선 탐색 (0) | 2023.05.15 |
---|---|
DFS (Depth First Search) 깊이 우선 탐색 (0) | 2023.05.15 |
Red-Black Tree (0) | 2023.05.15 |
BST (BinarySearchTree) (0) | 2023.05.15 |
PriorityQueue(우선순위 큐) (0) | 2023.05.15 |