1) 모든 노드를 방문하고자 하는 경우에 이 방법을 선택한다.
2) 깊이 우선 탐색(DFS)이 너비 우선 탐색(BFS)보다 좀 더 간단하다.
3) 검색 속도 자체는 너비 우선 탐색(BFS)에 비해서 느리다.
DFS는 스택 자료구조(혹은 재귀함수)를 이용한다.
1) 탐색 시작 노드를 스택에 삽입하고 방문 처리한다.
2) 스택의 최상단 노드에 방문하지 않은 인접한 노드가 하나라도 있으면 그 노드를 스택에 넣고 방문 처리한다. 방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼낸다.
3) 더 이상 2번의 과정을 수행할 수 없을 때까지 반복한다.
사진 출처 : https://code-lab1.tistory.com/15
void dfs(int x)
{
visited[x] = true;
cout << x << " ";
for (int i = 0; i < graph[x].size(); i++) // 인접한 노드 사이즈만큼 탐색
{
int y = graph[x][i];
if (!visited[y]) // 방문하지 않았으면 즉 visited가 False일 때 not을 해주면 True가 되므로 아래 dfs 실행
dfs(y); // 재귀적으로 방문
}
}
void bfs(int start) {
queue<int> q;
q.push(start); // 첫 노드를 queue에 삽입
visited[start] = true; // 첫 노드를 방문 처리
// 큐가 빌 때까지 반복
while (!q.empty()) {
// 큐에서 하나의 원소를 뽑아 출력
int x = q.front();
q.pop();
cout << x << ' ';
// 해당 원소와 연결된, 아직 방문하지 않은 원소들을 큐에 삽입
for (int i = 0; i < graph[x].size(); i++) {
int y = graph[x][i];
if (!visited[y]) {
q.push(y);
visited[y] = true;
}
}
}
}
인접 리스트 : O(N+E)
인접 행렬 : O(N²)
일반적으로 E(간선)의 크기가 N²에 비해 상대적으로 적다.
따라서, 인접 리스트 방식이 효율적이다.
#include <iostream>
using namespace std;
int n, m; //노드 개수: n, 간선 개수: m
int adj[10][10];
int main() {
cin >> n >> m;
for (int i=0; i<m; i++) {
int a, b;
cin >> a >> b;
adj[a][b] = 1; //무방향 그래프인 경우
adj[b][a] = 1;
}
}
vector<int> graph[5];
for(int i=0; i<m; i++){
int u,v;
cin >> u >> v;
graph[u].push_back(v); //무방향 그래프인 경우
graph[v].push_back(u);
}
1) 그래프의 모든 정점을 방문하는 문제
단순히 모든 정점을 방문하는 것이 중요한 문제의 경우 DFS, BFS 두 가지 방법 중 어느 것을 사용해도 상관없다.
2) 경로의 특징을 저장해둬야 하는 문제
예를 들면 각 정점에 숫자가 적혀있고 a부터 b까지 가는 경로를 구하는데 경로에 같은 숫자가 있으면 안 된다는 문제 등, 각각의 경로마다 특징을 저장해둬야 할 때는 DFS를 사용한다. (BFS는 경로의 특징을 가지지 못한다.)
3) 최단거리 구해야 하는 문제
미로 찾기 등 최단거리를 구해야 할 경우, BFS가 유리하다.
왜냐하면 깊이 우선 탐색으로 경로를 검색할 경우 처음으로 발견되는 해답이 최단거리가 아닐 수 있지만, 너비 우선 탐색으로 현재 노드에서 가까운 곳부터 찾기 때문에경로를 탐색 시 먼저 찾아지는 해답이 곧 최단거리기 때문이다.
이밖에도
https://devuna.tistory.com/32
https://velog.io/@cha-suyeon/알고리즘-깊이-우선-탐색DFS-과-너비-우선-탐색BFS
https://better-tomorrow.tistory.com/entry/DFS-BFS-이해하기
https://code-lab1.tistory.com/15