트리나 그래프를 탐색하는 기법 중 하나로, 시작 노드에서 자식의 노드들을 순서대로 탐색하면서 깊이를 우선으로 탐색하는 알고리즘이다.
깊이를 우선시하여 모든 경우의 수를 탐색하기 때문에 완전탐색 알고리즘에 속하기는 하지만, 항상 완전탐색으로 사용되지는 않는다.
DFS는 주로 반복문을 활용하거나, 재귀문을 통하여 구현된다.
DFS의 기본 탐색 과정은 특정 정점에서 시작하여 역추적(backtracking) 하기 전에 각 분기를 따라 가능한 한 멀리 탐색하는 것이다. 탐색하는 과정은 다음과 같다.
DFS는 모든 경로를 방문해야 할 경우 사용에 적합
DFS는 특정 시나리오에서 매우 유용할 수 있지만 항상 최선의 선택은 아니다. 해결하려는 특정 문제에 따라 BFS(Breadth-first Search)와 같은 다른 알고리즘이 더 적합할 수 있다
반복구현에서는 스택 데이터 구조를 사용하여 방문할 정점을 추적한다.
import java.util.*;
class DFS {
static ArrayList<Integer>[] adj; // adjacency list representation of the graph
static boolean[] visited; // array to keep track of visited vertices
public static void dfs(int v) {
Stack<Integer> stack = new Stack<>();
stack.push(v);
visited[v] = true;
while (!stack.isEmpty()) {
int vertex = stack.pop();
System.out.print(vertex + " ");
for (int neighbor : adj[vertex]) {
if (!visited[neighbor]) {
stack.push(neighbor);
visited[neighbor] = true;
}
}
}
}
public static void main(String[] args) {
int n = 5; // number of vertices
adj = new ArrayList[n];
visited = new boolean[n];
// initialize adjacency list and visited array
for (int i = 0; i < n; i++) {
adj[i] = new ArrayList<>();
visited[i] = false;
}
// add edges to the graph
adj[0].add(1);
adj[0].add(2);
adj[1].add(2);
adj[2].add(0);
adj[2].add(3);
adj[3].add(3);
for (int i = 0; i < n; i++) {
if (!visited[i]) {
dfs(i);
}
}
}
}
재귀 구현
import java.util.*;
class DFS {
static ArrayList<Integer>[] adj; // adjacency list representation of the graph
static boolean[] visited; // array to keep track of visited vertices
public static void dfs(int v) {
visited[v] = true;
System.out.print(v + " ");
for (int neighbor : adj[v]) {
if (!visited[neighbor]) {
dfs(neighbor);
}
}
}
public static void main(String[] args) {
int n = 5; // number of vertices
adj = new ArrayList[n];
visited = new boolean[n];
// initialize adjacency list and visited array
for (int i = 0; i < n; i++) {
adj[i] = new ArrayList<>();
visited[i] = false;
}
// add edges to the graph
adj[0].add(1);
adj[0].add(2);
adj[1].add(2);
adj[2].add(0);
adj[2].add(3);
adj[3].add(3);
for (int i = 0; i < n; i++) {
if (!visited[i]) {
dfs(i);
}
}
}
}
두 방법 모두 시간 및 공간 복잡도는 동일하지만 반복 구현은 일반적으로 호출 스택을 사용하지 않기 때문에 공간 측면에서 더 효율적이라고 간주된다.