깊이 우선 탐색과 너비 우선 탐색이란?
- 트리, 그래프를 탐색하는 두 가지의 알고리즘.
- 쉽게 설명하자면 미로 찾기.
S
/ \
A B
/ \ \
C D E
\ \
F(출구) G
- S에서 시작해서 F에서 탈출하는 미로가 있다고 할 때 (단, 낮은 순번부터 탐색한다고 가정)
- DFS: S - A - C - D - F(출구)
- C에서 이동할 곳이 없으므로 다시 A로 복귀
- BFS: S - A - B - C - D - E - F(출구)
- A, B를 탐색 후 다음 단계 탐색
- C, D, E를 탐색 후 다음 단계 탐색
- DFS: S - A - C - D - F(출구)
깊이 우선 탐색(Depth-First Search/DFS)
- 현재 위치에서 가장 깊은 곳 까지 탐색한 뒤 탐색이 종료되면 되돌아가서 다른 경로를 탐색하는 방식.
- 스택, 재귀 함수를 사용하여 구현한다.
- 메모리가 제한 적인 경우 BFS보다 유리
너비 우선 탐색(Breadth-First Search/BFS)
- 현재 위치에서 인접한 모든 위치를 방문하고 그 다음 레벨의 위치들을 순서대로 방문하는 탐색 방식
- 큐를 사용하여 구현한다.
- 최단 경로를 구하는 경우에 DFS보다 유리
예시 문제 - DFS와 BFS
https://www.acmicpc.net/problem/1260
설명
그래프를 DFS로 탐색한 결과와 BFS로 탐색한 결과를 출력하는 프로그램을 작성하시오. 단, 방문할 수 있는 정점이 여러 개인 경우에는 정점 번호가 작은 것을 먼저 방문하고, 더 이상 방문할 수 있는 점이 없는 경우 종료한다. 정점 번호는 1번부터 N번까지이다.
입력
첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사이에 여러 개의 간선이 있을 수 있다. 입력으로 주어지는 간선은 양방향이다.
출력
첫째 줄에 DFS를 수행한 결과를, 그 다음 줄에는 BFS를 수행한 결과를 출력한다. V부터 방문된 점을 순서대로 출력하면 된다.
- 그래프 작성
# 입력 예시
5 5 3
5 4
5 2
1 2
3 4
3 1
n, m, start = map(int, input().split())
# n: 노드의 수 (5)
# m: 간선의 수 (5)
# start: 시작 노드 (3)
# 노드의 번호를 key로, 연결된 다른 노드들의 list를 value로 하여 dictionary형태로 graph를 정의
graph = {i: [] for i in range(1, n + 1)}
# 입력값(간선)을 순회하여 graph 완성
for _ in range(m):
a, b = map(int, input().split())
graph[a].append(b)
graph[b].append(a)
# graph의 각 value를 정렬하여 작은 순번부터 방문하도록 함.
for key in graph :
graph[key].sort()
# graph == {1: [2, 3], 2: [1, 5], 3: [1, 4], 4: [3, 5], 5: [2, 4]}

- DFS 탐색

- 3번 노드에서 시작하여 연결된 노드 중 숫자가 더 낮은 1번 노드를 탐색
- 1번 노드와 연결된 2번 노드를 탐색
- 2번 노드와 연결된 5번 노드를 탐색
- 5번 노드와 연결된 4번 노드를 탐색
- 탐색 종료
- 방문 순서: 3 1 2 5 4
- BFS 탐색

- 3번 노드에서 시작하여 연결된 노드 중 숫자가 더 낮은 1번 노드를 탐색
- 3번 노드로 돌아와서 연결된 노드인 4번 노드를 탐색
- 3번 노드와 연결된 노드를 모두 탐색 했으므로 1번노드와 연결된 2번 노드를 탐색
- 1번 노드와 연결된 노드를 모두 탐색했으므로 4번 노드와 연결된 5번 노드를 탐색
- 탐색 종료
- 방문 순서: 3 1 4 2 5
어떻게 구현을 할까?
DFS는 재귀 함수를, BFS는 큐를 사용하면 된다!
- DFS
def dfs(graph, n, start, result=None):
if result is None:
result = []
# graph: 탐색할 그래프
# n: 탐색할 노드의 수
# start: 시작 노드
# result: 탐색 결과를 추적하여 반환할 변수
def dfs(graph, n, start, result=None):
if result is None:
result = []
result.append(start)
# result에 시작 노드를 추가하여 탐색을 했다는 것을 표시
def dfs(graph, n, start, result=None):
if result is None:
result = []
result.append(start)
if len(result) >= n or start not in graph:
return result
# 종료 조건 설정
# len(result) >= n: 모든 노드를 탐색한 경우
# start not in graph: 현재 탐색 중인 노드가 존재하지 않는 경우
def dfs(graph, n, start, result=None):
if result is None:
result = []
result.append(start)
if len(result) >= n or start not in graph:
return result
# graph == {1: [2, 3], 2: [1, 5], 3: [1, 4], 4: [3, 5], 5: [2, 4]}
for node in graph[start]:
if node not in result:
dfs(graph, n, node, result)
if len(result) >= n:
break
return result
# for node in graph[start]: - 현재 노드에 연결된 모든 노드를 순회한다.
# if node not in result - 만약 노드가 result에 추가되어 있지 않은 경우에 (즉, 아직 방문하지 않은 노드의 경우) 다음 단계로 이동
# dfs(graph, n, node, result) - 아직 방문하지 않은 노드에 대하여 탐색 수행
# if len(result) >= n: break - 모든 노드를 탐색한 경우에는 즉시 탐색을 종료
- DFS의 탐색 순서
- 3번 노드(시작점)/인접 노드[1, 4]
- 1번 노드/인접 노드[2, 3]
- 2번 노드/인접 노드[1, 5]
- 5번 노드/인접 노드[2, 4
- 4번 노드/인접 노드[5, 3] → 방문하지 않은 인접 노드가 없으므로 탐색 종료
- 5번 노드/인접 노드[2, 4
- 2번 노드/인접 노드[1, 5]
- 1번 노드/인접 노드[2, 3]
- 3번 노드(시작점)/인접 노드[1, 4]
- BFS
def bfs(graph, n, start):
visited = []
queue = [start]
# graph: 탐색할 그래프
# n: 탐색할 노드의 수
# start: 시작 노드
# queue라는 리스트에 시작 노드를 추가하고 탐색 시작
def bfs(graph, n, start):
visited = []
queue = [start]
while queue:
node = queue.pop(0)
# while queue: - queue의 원소가 존재하는 동안 반복 수행 (즉, 탐색할 공간이 남아있는 동안 계속 탐색)
# node = queue.pop(0) - 가장 앞에 있는 원소(다음으로 탐색할 노드)를 queue에서 제거하여 탐색 준비
def bfs(graph, n, start):
visited = []
queue = [start]
while queue:
node = queue.pop(0)
if node not in visited:
visited.append(node)
# 탐색할 노드(node)가 아직 방문하지 않은 노드일 경우(visited에 입력이 되지 않은 경우) visited에 해당 노드를 추가.
def bfs(graph, n, start):
visited = []
queue = [start]
while queue:
node = queue.pop(0)
# graph == {1: [2, 3], 2: [1, 5], 3: [1, 4], 4: [3, 5], 5: [2, 4]}
if node not in visited:
visited.append(node)
for neighbor in graph[node]:
if neighbor not in visited and neighbor not in queue:
queue.append(neighbor)
return visited
# node와 연결된 모든 인접 노드(neighbor)를 확인하여 다음 조건을 충족하는 경우 queue에 추가하여 반복 탐색
# neighbor not in visited - 인접 노드를 아직 방문하지 않은 경우
# neighbor not in queue - 인접 노드가 queue에 없는 경우(즉, 탐색 예정이 아닌 경우)
- BFS의 탐색 순서
- 초기 상태
- 시작 노드를 탐색 큐에 추가하여 탐색 시작
- 큐: [3]
- 방문: []
-
- 1단계
- 3번 노드를 방문하고 연결된 노드들을 탐색 큐에 추가
- 큐: [1, 4]
- 방문: [3]
- 2단계
- 1번 노드를 방문하고 연결된 노드들을 탐색 큐에 추가
- 큐: [4, 2]
- 방문: [3, 1]
- 3단계
- 4번 노드를 방문하고 연결된 노드들을 탐색 큐에 추가
- 큐: [2, 5]
- 방문: [3, 1, 4]
- 4단계
- 2번 노드를 방문하고 연결된 노드들을 탐색 큐에 추가 → 이미 모든 노드를 방문 했거나 탐색 큐에 있으므로 추가하지 않음
- 큐: [5]
- 방문: [3, 1, 4, 2]
- 5단계
- 5번 노드를 방문하고 탐색 큐가 비었으므로 탐색 종료
- 큐: []
- 방문: [3, 1, 4, 2, 5]
- 1단계
- 초기 상태
'알고리즘' 카테고리의 다른 글
| 비트 연산자 (1) | 2025.08.04 |
|---|---|
| 재귀 함수 (3) | 2025.08.04 |
| 프로그래머스 - 무인도 여행 (1) | 2024.11.03 |
| 알고리즘 코드카타 130 - 두 원 사이의 정수 쌍 (0) | 2024.08.17 |
| 알고리즘 코드카타 129 - 우박수열 정적분 (0) | 2024.08.15 |