All Articles

[Algorithm] Cycle Detecting

DFS를 이용한 방법

backedge: 자기 자신 또는 자기 자신의 부모로 향하는 엣지

backedge를 찾는다 ⇒ DFS 재귀 함수를 수행하면서 한번 방문한 노드인데, 그 함수가 리턴되기 전에 그 노드를 또 방문하게 된다면, cycle이 존재하는 것!

isCyclic 재귀함수 내부에서

  • current node를 visited로 체크한 후, recursion stack 배열에 체크한다
  • current node에 인접하고, 방문되지 않은 모든 node를 가지고 같은 재귀 함수를 수행한다

    • 여기서 true를 리턴하는 노드가 있으면 true를 리턴한다
  • current node에 인접하고, 방문되었고, recursion stack에 들어있는 노드가 있으면 true를 리턴한다

    • 이미 방문 되었다는 건 그 노드에 대해 dfs 가 수행되었다는 뜻인데, 그 이후에도 recursion stack에 들어가 있다는 건 아직 그 dfs가 리턴되지 않은 상태에서 또 다시 방문하게 된 것 ⇒ cycle 존재한다는 것
  • recursion stack에서 current node를 뺀다 (false로 바꾼다)
// 사이클 없는 그래프인지 판단하기 위한 함수
public boolean isValid(int n, int[][] edges){
	boolean[][] adj = new boolean[n][n];
	// edge 처리
	// adj 처리
	...

	boolean[] visited = new boolean[n];
	boolean[] stack = new boolean[n];
	for(int i=0; i<n; i++){
		if(!visited[i] && isCyclic(adj, i, visited, stack) return false;
	}
	return true;
}

boolean isCyclic(boolean[][] adj, int i, boolean[] visited, boolean[] stack){
	visited[i] = true;
	stack[i] = true;
	for(int j=0; j < adj[i].length; j++){
		if(!visited[j]) {
			if(isCyclic(adj, j, visited, stack) return true;
		} else if(stack[i]) return true;
	}
	stack[i] = false;
	return false;
}

BFS를 이용한 방법

1) with topological sort

Topological Sorting을 함께 이용한다 ⇒ 노드 별 유입 edge의 개수를 추적해야한다

사이클이 있다면, topological sort를 수행하여 노드를 방문했을 때 방문되지 않고 남아있는 노드가 존재할 것이다.

사이클이 있는 경우 모든 노드를 다 방문해도 유입 엣지가 지워지지 않는 노드가 있을 것이기 때문!

즉, (number of vertices present in the topological ordering) != total number of vertices present in the graph 이면 사이클이 존재할 것이다.

HashMap<Integer, Set<Integer>> adjList = new HashMap<>();
//adj 및 edgeCount 처리 되었다고 가정, edgeCount는 유입엣지

visitedCount = 0;

for(int i=0; i<n; i++){
	if(edgeCount[i] == 0) queue.add(i);
}

while(!queue.isEmpty()){
	int curr = queue.poll();
	visitedCount ++;
	Set<Integer> adj = null;
	if(adjList.containsKey(curr)) {
		adj = adjList.get(curr);
		// 인접한 노드들 
		for(int num: adj){
			edgeCount[num]--; //curr에서 num으로 유입되는 엣지 지움
			if(edgeCount[num] == 0) queue.add(num);
		}
	}

return visitedCount == n;

Kruskal Algorithm 이용

방문되지 않은 노드들 중 최단 거리의 edge를 가진 노드들, 즉 다음에 방문할 노드 u,v가 같은 root를 가지고 있다면 사이클이 있는 것

(u, v)가 없다고 가정한 상황인데, 이미 두 집합의 루트가 같다

= root → u, u → root, v → root, root → v가 다 존재한다 (undirected graph)

⇒ u → v가 추가되면 v → u → v 의 path가 만들어진다 ⇒ 사이클이 생긴다

즉, 두 집합의 FIND 연산 결과가 같으면 두 집합이 연결되어 있다

for each unvisited edge (u, v) in E
{
    if(Find(u) = Find(v)) // u and v belong to the same set already
    {
        print "Cycle Detected";
        break;
    } else {
        Union(u, v); // put u and v in the same set
    }
}