23.07.13 스터디, 깊이 우선 탐색 알고리즘 학습
트리나 그래프에서 사용되는 탐색 알고리즘입니다. 이 방법은 시작 정점에서 시작하여 각 분기점에 대해 최대한 깊이 들어가면서 탐색하고, 더 이상 진행할 수 없게 되면 이전 분기점으로 돌아와서 다른 경로를 탐색하는 방식으로 진행됩니다.
주로 스택 또는 재귀를 이용하여 구현됩니다.
DFS는 많은 문제 해결에서 사용되며, 그래프가 사이클을 포함하지 않는 경우 적합합니다.
그래프가 넓고 노드 수가 많을 경우,
DFS는 탐색 과정에서 불필요한 방향으로 깊이 들어가기 때문에 시간이 오래 걸릴 수 있습니다. 이런 경우에는 BFS(Breadth-First Search, 너비 우선 탐색)와 같은 다른 알고리즘을 사용할 수 있습니다.
쉽게 설명하면, 이제 말하는 것이 dfs가 돌아가는 로직이라고 생각하시면 됩니다. 좌측부터 채워지며, 아래까지 쭈욱 채우고, 이후 옆으로 옮겨가서 채워주고, 이런식으루 좌->우로 옮겨가며 최하단의 노드까지 채워주면 끝이 되는 것입니다.
DFS는 한 번 방문한 노드를 다시 방문하면 안 되므로, 노드 방문 여부를 체크할 배열이 필요하며, 그래프는 보통 인접 리스트로 표현합니다. 그리고 DFS의 탐색 방식은 후입선출(스택)의 특성을 가지므로 스택을 사용하는 경우가 종종 있습니다.
BUT, 필자는 편의를 위해 스택으로 예시를 들었으나, DFS 구현은 스택보다는 스택의 성질을 갖고 있는 재귀함수로 많이 구현합니다. 즉, 대부분의 DFS는 재귀 함수로 구현 합니다.
위 그림을 보시면 조금 더 이해하기 쉬울 것입니다. 간선이 이렇게 연결되어 있을경우엔 다르게 작용하는 것입니다.
1. 스택의 최상단 노드를 확인해야합니다.
2. 최상단 노드에게 방문하지 않은 인접 노드가 있으면 그 노드를 스택에 넣고,
방문처리 방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 제거합니다.
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class DFSUsingStack {
static boolean[] visited = new boolean[8];
static List<List<Integer>> graph = new ArrayList<>();
public static void main(String[] args) {
initializeGraph();
dfs(1);
}
private static void initializeGraph() {
for (int i = 0; i < 8; i++) {
graph.add(new ArrayList<>());
}
graph.get(1).add(2);
graph.get(1).add(3);
graph.get(2).add(1);
graph.get(2).add(4);
graph.get(2).add(5);
graph.get(3).add(1);
graph.get(3).add(4);
graph.get(3).add(6);
graph.get(4).add(2);
graph.get(4).add(3);
graph.get(4).add(5);
graph.get(4).add(7);
graph.get(5).add(2);
graph.get(5).add(4);
graph.get(5).add(7);
graph.get(6).add(3);
graph.get(6).add(7);
graph.get(7).add(4);
graph.get(7).add(5);
graph.get(7).add(6);
}
private static void dfs(int start) {
Stack<Integer> stack = new Stack<>();
stack.push(start);
visited[start] = true;
System.out.print(start + " ");
while (!stack.isEmpty()) {
int currentNode = stack.peek();
List<Integer> adjNodes = graph.get(currentNode);
boolean hasUnvisitedNode = false;
for (Integer node : adjNodes) {
if (!visited[node]) {
stack.push(node);
visited[node] = true;
System.out.print(node + " ");
hasUnvisitedNode = true;
break; // 들어갔으니 중지한다
}
}
if (!hasUnvisitedNode) {
stack.pop();
}
}
}
}