
DFS는 Depth-First Search로 깊이 우선 탐색이라 한다.
BFS는 Breadth-First Search로 너비 우선 탐색이라 한다.
용어의 정의를 알면 어느 정도 감이 잡히므로 영어로 된 용어도 외울 수 있으면 외우도록 하자.
- 미로를 탐색할 때 한 방향으로 갈 수 있을 때까지 가다가 더 이상 갈 수 없 게 되면 다시 가장 가까운 갈림길로 돌아와서 다른 방향으로 다시 탐색을 진행하는 방법과 유사하다.
- 즉, 넓게(wide) 탐색하기 전에 깊게(deep) 탐색하는 것이다.
- 사용하는 경우 : 모든 노드를 방문하고자 하는 경우에 이 방법을 선택한다.
- 자기 자신을 호출하는 순환 알고리즘 형태를 가지고 있다.
- 전위 순회(Pre-Order Traversals)를 포함한 다른 형태의 트리 순회는 모두 DFS의 한 종류이다.
- 깊이 우선 탐색(DFS)이 너비 우선 탐색(BFS)보다 좀 더 간단하다.
- 단순 검색 속도 자체는 너비 우선 탐색(BFS)에 비해서 느리다.

- 시작 정점으로부터 가까운 정점을 먼저 방문하고 멀리 떨어져 있는 정점을 나중에 방문하는 순회 방법이다.
- 즉, 깊게(deep) 탐색하기 전에 넓게(wide) 탐색하는 것이다.
- 사용하는 경우 : 두 노드 사이의 최단 경로 혹은 임의의 경로를 찾고 싶을 때 이 방법을 선택
- 직관적이지 않다. (시작 노드에서 시작해서 거리에 따라 단계별로 탐색한다.)
- 어떤 노드를 방문했었는지 여부를 반드시 검사 해야한다.
- 이를 검사하지 않을 시 무한 루프에 빠질 위험이 있다.

| DFS | BFS |
|---|---|
| 1. 순환 호출 이용 | 큐를 이용 |
| 2. 명시적인 스택이용 |
파이썬의 경우 큐, 스택을 활용하기 위해서 List와 deque(popleft를 활용해 스택처럼 사용) 자료형을 이용할 수 있는데 이때 deque를 사용하는 것이 List를 사용하는 것보다 성능적인 측면에서 유리하다. 또한 List에서는 없는 popleft, appendleft 메서드를 사용할 수 있다는 장점이 있다.
위의 구현 방법을 토대로 DFS는 순환 호출(재귀 함수)를 이용하여 코딩, BFS는 파이썬의 List 자료형을 이용해 구현하였다.
DFS는 다음과 같이 구현
def dfs(graph, v, visited):
visited[v] = True
for i in graph[v]:
if not visited[i]:
dfs(graph, i, visited)
return
def dfs(graph, v):
graph[v][v] = -1 # 방문한 노드는 방문했다고 표시
print(v, end=" ")
for i in range(1, n + 1):
# 간선이 있고, 해당 노드에 방문학 적이 없다면
if graph[v][i] == 1 and graph[i][i] != -1:
dfs(graph, i) # 재귀를 사용해 dfs 구현
return
BFS는 다음과 같이 구현
def bfs(graph, v):
queue = [] # bfs는 큐를 이용해서 쉽게 구현
queue.append(v)
graph[v][v] = -1 # 방문한 노드는 방문했다고 표시
while queue: # 원소가 하나라도 있을시 True, 원소가 하나도 없으면 False
v = queue.pop(0)
print(v, end=" ")
for i in range(1, n + 1):
# 간선이 있고, 해당 노드에 방문학 적이 없다면
if graph[v][i] == 1 and graph[i][i] != -1:
queue.append(i)
graph[i][i] = -1 # 방문한 노드는 방문했다고 표시
return
이 때 방문했다는 사실을 따로 변수를 만들지는 않고 graph의 대각 원소를 방문했다는 사실을 파악하기 위해 사용한다.
def dfs(graph, v): # 순환 함수 말고도 stack 형식을 이용해서 구현해낼 수 있다.
graph[v][v] = -1 # 방문한 노드는 방문했다고 표시
print(v, end=" ")
for i in range(1, n + 1):
# 간선이 있고, 해당 노드에 방문학 적이 없다면
if graph[v][i] == 1 and graph[i][i] != -1:
dfs(graph, i) # 재귀를 사용해 dfs 구현
return
def bfs(graph, v):
queue = [] # bfs는 큐를 이용해서 쉽게 구현
queue.append(v)
graph[v][v] = -1 # 방문한 노드는 방문했다고 표시
while queue: # 원소가 하나라도 있을시 True, 원소가 하나도 없으면 False
v = queue.pop(0)
print(v, end=" ")
for i in range(1, n + 1):
# 간선이 있고, 해당 노드에 방문학 적이 없다면
if graph[v][i] == 1 and graph[i][i] != -1:
queue.append(i)
graph[i][i] = -1 # 방문한 노드는 방문했다고 표시
return
n, m, v = map(int, input().split())
graph = [[0 for _ in range(n + 1)] for _ in range(n + 1)]
for _ in range(m):
v1, v2 = map(int, input().split())
graph[v1][v2] = graph[v2][v1] = 1
dfs(graph, v)
for i in range(1, n + 1):
graph[i][i] = 0
print()
bfs(graph, v)
DFS BFS의 기본을 이용하는 문제들이므로 개념을 익히기에 좋다. DFS, BFS 두 방식 모두 이용해 해결해보도록 한다.
토마토 문제에서 BFS를 이용해 푸는 것을 연습할 수 있다. 두 문제의 차이는 배열의 차원이다. 첫 번째 토마토 문제는 2차원 배열을 이용하고, 2번째 문제는 3차원 배열을 이용한다. 차례대로 풀어보는 것을 추천한다.