Cycle Detection
Updated:
사이클 탐지기법은 dfs를 사용하는 방법과 union-find 방식이 있는데 여기서는 dfs를 사용한 방법을 소개하고자 한다.
dfs를 실행하게 되면 dfs spanning tree가 만들어지는데, 사이클이 있다는 것은 tree에서 back edge(역방향 간선)가 존재한다는 것이다.
즉, 이 back edge를 찾게되면 사이클이 있다는 것을 알 수 있다.
Back edge
Back edge란, 다음의 그림에서 빨간색 선이다.
정점 1, 2, 5가 사이클을 형성하는 것을 볼 수 있다. 사이클을 찾기 위해서는 Back edge를 찾아야 한다.
Undirected Graph
방향성이 없는 그래프는 이전에 방문한 노드를 다시 방문하면 cycle이 존재한다.(단, 직전 방문한 노드는 해당되지 않는다.)
vector<vector<int>> edges;
vector<bool> visited;
bool isCycle(int u, int parent) {
visited[u]=true;
for(int v : edges[u]) {
if(!visited[v]) {
if(isCycle(v,u))
return true;
}
else if(v!=parent) { // 직전 방문한 노드가 아니면서 방문한 적이 있는 노드라면
return true;
}
}
return false;
}
Directed Graph
방향성이 있는 그래프는 조금 다르다. 무향 그래프와 다르게 이전 방문한 노드와 사이클을 이룰 수 있다.
그리고 무향 그래프에서 사용했던 코드를 다시 사용할 수는 없다.
예를들면, 아래 그래프에서 dfs로 1 → 2 → 3 → 4까지 내려간 후 길이 없어 2번 정점까지 리턴 될 것이다.
이제, 2번에서 5번으로 이동 후 4번으로 가려고 했을 때 4번이 이미 방문한 지점이라 위의 코드에서 true를 반환하게 될 것이다. 하지만 실제로는 사이클이 아니다.
그래서 이번에는 stack처럼 사용할 다른 배열의 공간이 필요하다. 이것을 recStack[]
이라 하자.
처음 1번에서 시작하여 dfs를 진행한다.
차례대로 4번 정점까지 들어가면 두 배열의 상태는 다음과 같다.
- visited[] : [1,1,1,1,0]
- recStack[] : [1,1,1,1,0]
다시 반환되어 2번 정점까지 되돌아온다. 이때 배열의 상태는 다음과 같다.
- visited[] : [1,1,1,1,0]
- recStack[] : [1,1,0,0,0]
다음 2번에서 5번으로 진행한다.
이때 배열의 상태는 다음과 같다.
- visited[] : [1,1,1,1,1]
- recStack[] : [1,1,0,0,1]
마지막으로 5번 정점에서 1번 정점으로 진행하면 recStack[1]=1이므로 true가 반환되어 이 그래프가 사이클이 있다는 것을 판단할 수 있다.
vector<vector<int>> edges;
vector<int> visited;
vector<int> recStack;
bool isCycle(int u) {
visited[u]=true;
recStack[u]=true;
for(int v : edges[u]) {
if(!visited[v]) {
if(isCycle(v))
return true;
}
else if(recStack[v]) { // 스택에 있는 정점이라면 사이클이 존재
return true;
}
}
recStack[u]=false; // 사이클을 형성하지 못하는 노드는 스택에서 제거
return false;
}
Comments