반응형
DFS (깊이 우선 탐색, Depth First Search)
DFS는 그래프에서 깊은 부분을 우선적으로 탐색합니다.
한 정점에서 시작해 다음 경로로 넘어가기 전에 해당 경로를 완벽하게 탐색할 때 사용합니다.
특징
- 스택 자료 구조에 기초하며, 구현할 때 재귀 함수로 구현하는 것이 좀더 간편합니다.
- DFS를 구현할 때, 그래프 탐색의 경우 어떤 노드를 방문했었는지 여부를 검사하지 않을 시 무한 루프에 빠질 위험이 있으므로 반드시 검사해야합니다. ( visited[index] = true; )
- 미로를 탐색할 때, 해당 분기에서 갈 수 있을 때까지 계속 가다가 더 이상 갈 수 없게 되면 다시 가장 가까운 갈림길로(새로운 분기)로 돌아와서 다른 방향으로 다시 탐색을 진행하는 방법과 유사합니다.
- 모든 경우를 하나하나 다 탐색을 해야될경우 사용합니다. ( 순열, 조합 구현 )
- 검색 속도 자체는 BFS에 비해 느립니다.
탐색 과정
( 파란색 : 이동방향, 초록색 : 탐색 순서 )
- 제일 상위 노드 "1"부터 시작하여 방문하지 않은 인접한 "2, 3"노드 중 가장 작은 노드 "2"를 방문합니다. ( 1 -> 2 )
- "2"에서 방문하지 않은 인접한 노드 "4"를 방문합니다. ( 1 -> 2 -> 4 )
- "4"에서 인접한 노드는 "2번"노드 인데 이미 방문한 노드이므로 "4"번 노드를 호출한 부모 노드 "2"로 돌아갑니다.
- "2"에서 인접한 노드는 "1, 4"인데 둘 다 이미 방문한 노드이므로 "2"번 노드를 호출한 부모 노드 "1"로 돌아갑니다.
- "1"번 노드와 인접한 노드는 "2, 3"인데 2번은 이미 방문 하였으므로 방문하지 않은 "3"번 노드를 방문합니다. ( 1 -> 2 -> 4 -> 3 )
- "3"번 노드와 인접한 노드는 "5, 6"인데 둘다 방문하지 않았으므로 가장 작은 노드 "5"번 노드를 방문합니다. ( 1 -> 2 -> 4 -> 3 -> 5 )
- "5"번 노드와 인접한 "7"번 노드를 방문합니다. ( 1 -> 2 -> 4 -> 3 -> 5 -> 7 )
- "7"번 노드에 더이상 인접한 노드가 없으므로 부모 노드 "5"로 돌아가고 "5"도 "7"을 이미 방문했으므로 부모 노드 "3"으로 돌아갑니다.
- "3"번 노드에서 방문하지 않은 "6"번 노드를 방문합니다. ( 1 -> 2 -> 4 -> 3 -> 5 -> 7 -> 6 )
- 역순으로 돌아가 방문하지 않은 노드가 있는지 확인하고 다 방문했다면 해당 방문 순서로 탐색 결과를 반환하고 종료합니다.
탐색 순서 : 1 -> 2 -> 3 -> 4 -> 5 -> 7 -> 6
구현 코드
public class Dfs {
public static void main(String[] args) {
int start = 1; // 시작 노드
dfs(start);
}
// 각 노드가 연결상태를 2차원 배열로 표현
// 해당 graph의 인덱스번호의 배열은 해당 노드에서 연결된 노드를 갖는다.
// 0번 1번 2번 3번 4번 5번 6번 7번
static int[][] graph = {{}, {2, 3}, {4}, {5, 6}, {2}, {5, 7}, {3}, {7}};
// 방문처리에 사용 할 배열
static boolean[] visited = new boolean[graph.length];
static void dfs(int index) {
// 현재 노드를 방문 처리
visited[index] = true;
// 방문 노드 출력
System.out.print(index + " -> ");
// 방문한 노드에 인접한 노드 찾기
for (int node : graph[index]) {
// 인접한 노드가 방문한 적이 없다면 재귀 호출
if(!visited[node]) {
dfs(node);
}
}
}
}
BFS (너비 우선 탐색, Bread First Search )
BFS는 그래프에서 가장 가까운 부분을 우선적으로 탐색합니다.
한 정점에서 가장 인접한 노드를 먼저 넓게 탐색하면서 깊이 내려가므로 DFS와는 정반대입니다.
특징
- 재귀적으로 동작하지 않습니다.
- BFS도 DFS처럼 노드 방문 여부를 반드시 검사해야합니다.
- 방문한 노드들을 차례로 저장한 후 꺼낼 수 있는 선입 선출 자료구조 Queue를 사용합니다. (선입선출 원칙으로 탐색)
- 최단경로(다익스트라, 플로이드)를 찾는데 활용합니다.
탐색 과정
( 파란색 : 이동방향, 초록색 : 탐색 순서 )
- 제일 상위 "1"번노드에서 시작하여 "1"번 노드를 큐에 넣고 방문 처리합니다.
- "1"번 노드와 인접한 "2, 3"번 노드를 큐에 넣고 방문 처리 합니다. ( 2 , 3 )
- 큐에서 "2"번 노드를 꺼내고 "2"번 노드와 인접한 방문하지 않은 "4"번 노드를 큐에 넣고 방문 처리 합니다. ( 3, 4 )
- 큐에서 "3"번 노드를 꺼내고 "3번" 노드와 인접한 방문하지 않은 "5, 6"번 노드를 큐에 넣고 방문 처리 합니다. ( 4, 5, 6 )
- 큐에서 "4"번 노드를 꺼내고 방문하지 않은 인접한 노드가 없으므로 넘어갑니다. ( 5, 6 )
- 큐에서 "5"번 노드를 꺼내고 "5"번과 인접한 방문하지 않은 "7"번 노드를 큐에 삽입하고 방문 처리 합니다. ( 6, 7 )
- 큐에서 "6"번 노드를 꺼내고 방문하지 않은 인접한 노드가 없으므로 넘어 갑니다. ( 7 )
- "7"번 노드도 인접한 노드가 없으므로 큐에서 꺼냅니다.
탐색 순서 ( 큐에 들어간 순서 ) : 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7
구현 코드
import java.util.LinkedList;
import java.util.Queue;
public class Bfs {
public static void main(String[] args) {
// 각 노드가 연결상태를 2차원 배열로 표현
int[][] graph = {{}, {2, 3}, {4}, {5, 6}, {2}, {5, 7}, {3}, {7}};
// 방문처리에 사용 할 배열
boolean[] visited = new boolean[graph.length];
// 시작 노드
int start = 1;
// 큐 생성
Queue<Integer> queue = new LinkedList<>();
queue.add(start);
// 현재 노드를 방문 처리
visited[start] = true;
// 큐가 비었을 때 까지 반복
while (!queue.isEmpty()) {
// 큐에서 하나의 원소를 뽑아 출력
int node = queue.poll();
System.out.print(node + " -> ");
// 인접한 노드 중 아직 방문하지 않은 원소들을 큐에 삽입
for (int index : graph[node]) {
if (!visited[index]) {
queue.add(index);
visited[index] = true;
}
}
}
}
}
'◼ CS 기초 지식 > [알고리즘]' 카테고리의 다른 글
[알고리즘] 그리디 알고리즘(탐욕법) (2) | 2023.01.19 |
---|---|
[알고리즘] 백트래킹(Back Tracking)이란? (0) | 2023.01.18 |
[알고리즘] 시간 복잡도(Time Complexity)란? (0) | 2023.01.12 |
[알고리즘] 누적합, 구간합 구하기 (0) | 2023.01.12 |
[알고리즘] 캐시 교체 알고리즘 - LRU, LFU (0) | 2023.01.11 |