그래프나 트리와 같은 자료 구조에서 한 노드에서 시작해 인접한 노드들을 먼저 모두 탐색하는 알고리즘
- 가까운 노드부터 '수평적으로' 탐색하는 것이 특징
DFS는 스택 또는 재귀 사용하고, 최단 경로 보장 불가함
큐는 먼저 들어간 데이터가 먼저 나오는 FIFO(First-In, First-Out) 방식이므로, 시작 노드로부터 가까운 노드를 먼저 탐색하는 BFS의 원리와 정확히 일치
파이썬의 collections.deque는 양방향 큐로, 노드를 넣고 빼는 작업이 매우 효율적이어서 BFS 구현에 자주 사용
구현 과정
일반적인 BFS 구현 방식은 오직 큐를 이용한 반복문 방식임. 다양한 구현 방법을 찾는 경우, 큐를 배열이나 리스트로 구현하는 방법 등을 생각해볼 수 있지만, 근본적인 탐색 원리는 동일함.
최단 경로 탐색:
탐색의 안정성:
BFS를 사용했을 때 탐색 순서는 어떻게 되는가?
A
/ \
B C
| |
D E
\ /
F
다음과 같은 그래프가 있다.
그래프는 노드와 노드 사이의 연결로 이루어져 있다.
너비 우선 탐색(BFS)을 이용해 모든 노드를 방문하는 순서를 구하라.
graph = {
'A': ['B', 'C'],
'B': ['D'],
'C': ['E'],
'D': ['F'],
'E': ['F'],
'F': []
}
--> 같은 레벨(거리)에 있는 노드들을 먼저 방문하는 방식
from collections import deque # BFS에서 사용할 큐 자료구조 import
def bfs(graph, start):
visited = set() # 이미 방문한 노드를 기록하는 집합
queue = deque([start]) # BFS는 큐(Queue) 사용, 시작 노드를 넣음
order = [] # 방문 순서를 저장할 리스트
while queue: # 큐가 빌 때까지 반복
node = queue.popleft() # 큐에서 가장 앞에 있는 노드를 꺼냄 (FIFO)
if node not in visited: # 아직 방문하지 않은 노드이면
visited.add(node) # 방문 표시
order.append(node) # 방문 순서를 리스트에 저장
# 현재 노드의 인접 노드들을 확인
# 작은 번호 순서대로 큐에 추가 (옵션: 테스트에서 작은 번호 먼저 방문)
for neighbor in sorted(graph[node]):
if neighbor not in visited: # 이미 방문한 노드는 큐에 넣지 않음
queue.append(neighbor) # 다음에 방문할 노드로 큐에 추가
return order # BFS 탐색 순서 반환
# ---------------------
# 입력 처리
# ---------------------
n, m = map(int, input().split()) # 정점 개수(n)와 간선 개수(m) 입력
graph = {i: [] for i in range(1, n+1)} # 1~n까지의 정점을 키로 하는 빈 리스트 생성
for _ in range(m):
u, v = map(int, input().split()) # 간선 연결 정보 입력
graph[u].append(v) # u->v 연결
graph[v].append(u) # v->u 연결 (무방향 그래프)
start = int(input()) # 시작 정점 입력
# BFS 실행
result = bfs(graph, start)
print(' '.join(map(str, result))) # 방문 순서 출력
...
--> 최종 방문 순서는 A -> B -> C -> D -> E -> F
BFS의 가장 대표적인 활용 예시로, 가중치가 없는 그래프에서 두 노드 사이의 최단 경로를 찾는 데 사용됨. 이는 BFS가 시작 노드로부터 같은 거리에 있는 모든 노드를 먼저 탐색하는 '너비 우선' 방식 때문.