이번에는 너비 우선 탐색(BFS)과 깊이 우선 탐색(DFS) 알고리즘에 대해 알아보자.
이 두 가지 탐색 방법은 그래프나 트리 구조에서 데이터를 탐색하거나 순회할 때 자주 사용되는 기본 알고리즘이다.
너비 우선 탐색은 주로 그래프나 트리에서 최단 경로를 찾거나, 특정 레벨에 있는 노드들을 탐색할 때 사용된다.
BFS는 먼저 시작 노드에서 가까운 노드들을 차례로 탐색하며, 큐(queue)를 사용하여 구현된다.
BFS는 다음과 같은 단계로 동작한다:
BFS의 시간 복잡도는 O(V + E)이다. 여기서 V는 노드의 수, E는 간선의 수를 의미한다.
BFS는 노드와 간선의 개수에 비례하여 시간이 소요된다.
아래는 BFS를 구현한 간단한 예제 코드이다.
이 코드에서는 그래프에서 특정 노드를 시작으로 너비 우선 탐색을 수행한다.
import java.util.*;
public class BFSExample {
public static void bfs(int startNode, Map<Integer, List<Integer>> graph) {
Queue<Integer> queue = new LinkedList<>();
Set<Integer> visited = new HashSet<>();
queue.add(startNode);
visited.add(startNode);
while (!queue.isEmpty()) {
int currentNode = queue.poll();
System.out.print(currentNode + " ");
for (int neighbor : graph.get(currentNode)) {
if (!visited.contains(neighbor)) {
queue.add(neighbor);
visited.add(neighbor);
}
}
}
}
public static void main(String[] args) {
Map<Integer, List<Integer>> graph = new HashMap<>();
graph.put(1, Arrays.asList(2, 3, 4));
graph.put(2, Arrays.asList(5, 6));
graph.put(3, Arrays.asList(7, 8));
graph.put(4, Arrays.asList(9));
graph.put(5, new ArrayList<>());
graph.put(6, new ArrayList<>());
graph.put(7, new ArrayList<>());
graph.put(8, new ArrayList<>());
graph.put(9, new ArrayList<>());
bfs(1, graph); // 출력: 1 2 3 4 5 6 7 8 9
}
}
깊이 우선 탐색은 가능한 깊은 노드까지 탐색을 진행한 후, 더 이상 갈 수 없는 경우 다시 이전 노드로 돌아가는 방식으로 탐색을 진행한다.
DFS는 주로 재귀를 통해 구현되며, 백트래킹(backtracking) 문제 해결에 자주 사용된다.
DFS는 다음과 같은 단계로 동작한다:
DFS의 시간 복잡도 역시 O(V + E)이다.
노드와 간선을 모두 탐색하기 때문에 BFS와 동일한 시간 복잡도를 가진다.
아래는 DFS를 재귀적으로 구현한 간단한 예제 코드이다.
이 코드에서는 그래프에서 특정 노드를 시작으로 깊이 우선 탐색을 수행한다.
import java.util.*;
public class DFSExample {
public static void dfs(int currentNode, Map<Integer, List<Integer>> graph, Set<Integer> visited) {
visited.add(currentNode);
System.out.print(currentNode + " ");
for (int neighbor : graph.get(currentNode)) {
if (!visited.contains(neighbor)) {
dfs(neighbor, graph, visited);
}
}
}
public static void main(String[] args) {
Map<Integer, List<Integer>> graph = new HashMap<>();
graph.put(1, Arrays.asList(2, 3, 4));
graph.put(2, Arrays.asList(5, 6));
graph.put(3, Arrays.asList(7, 8));
graph.put(4, Arrays.asList(9));
graph.put(5, new ArrayList<>());
graph.put(6, new ArrayList<>());
graph.put(7, new ArrayList<>());
graph.put(8, new ArrayList<>());
graph.put(9, new ArrayList<>());
Set<Integer> visited = new HashSet<>();
dfs(1, graph, visited); // 출력: 1 2 5 6 3 7 8 4 9
}
}
경로 탐색
BFS는 최단 경로를 찾는 문제에 자주 사용되며, DFS는 특정 경로를 찾거나 모든 가능한 경로를 탐색해야 하는 문제에 유용하다.
그래프 순회
BFS와 DFS는 그래프 순회에서 각각 다른 방식으로 그래프를 탐색하므로, 문제의 특성에 따라 적합한 방법을 선택할 수 있다.
백트래킹
DFS는 백트래킹 알고리즘의 핵심으로, 가능한 모든 해결책을 탐색하는데 사용된다.
예를 들어, 퍼즐 문제나 조합 문제에서 활용된다.
탐색 목적
최단 경로를 찾고자 한다면 BFS를, 모든 경로를 탐색하거나 특정 깊이까지 탐색하고자 한다면 DFS를 사용하는 것이 좋다.
메모리 사용
BFS는 큐에 모든 노드를 저장해야 하므로 메모리 사용이 많다.
반면 DFS는 재귀 호출을 사용하므로 상대적으로 적은 메모리를 사용한다.
BFS와 DFS는 모두 중요한 탐색 알고리즘이며, 상황에 맞게 잘 선택하고 활용하면 효율적으로 문제를 해결할 수 있을 것이다.
이 글을 통해 BFS와 DFS에 대해 더 깊이 이해할 수 있기를 바란다.