BFS는 가까운 노드부터 탐색하는 알고리즘이다. DFS는 최대한 멀리 있는 노드를 우선으로 탐색한다면 BFS는 그 반대다. BFS의 구현은 선입선출 방식인 큐 자료구조를 이용하는 것이 정석이다. 인접한 노드를 큐에 넣으면 자연스럽게 먼저 들어온 것이 먼저 나가므로 가까운 노드부터 탐색을 진행할 수 있다.
방문처리는 스택에 한번 삽입되어 처리된 노드가 다시 삽입되지 않도록 체크하는 것을 의미한다. 방문 처리를 통해 각 노드를 한 번씩만 처리할 수 있다.
* 단순 출력문제가 아닐 경우에는 큐에서 노드를 꺼낼 시 해당 노드에 대해 문제에서 요구하는 특정 동작을 수행한다.
다음 그래프를 DFS 알고리즘으로 탐색해보자. 이때 탐색 시작 노드는 1번 노드이다. 인접 노드가 여러 개일 때는 일반적으로 번호가 낮은 것부터 우선한다.
step 1)
step 2)
step 3)
step 4)
step 5)
step 6)
step 7)
결과적으로 1 → 2 → 3 → 8 → 7 → 4 → 5 → 6 순서가 된다.
너비 우선 탐색 알고리즘인 BFS는 큐 자료구조에 기초한다. 수행함에 있어서 O(N)의 시간복잡도를 가진다.
from collections import deque
# BFS 메서드 정의
def bfs(start, visited):
q = deque([start]) # 큐 구현을 위해 deque 라이브러리 사용
visited.add(start) # 시작 노드 방문처리
# 큐가 빌 때까지 반복
while q:
v = q.popleft() # 큐에서 하나 원소를 뽑아 출력
print(v, end=' ')
# 해당 원소와 연결된 아직 방문하지 않은 원소들을 큐에 삽입
for i in graph[v]:
if i in visited:
q.append(i)
visited.add(i)
visited = set()
graph = {
1: [2, 3, 8], # 1번 노드에 2, 3, 8번 노드가 연결되어있다.
2: [1, 7],
3: [1, 4, 5],
4: [3, 5],
5: [3, 4],
6: [7],
7: [2, 6, 8],
8: [1, 7]
}
dfs(1)
#include <stdio.h>
int graph[1001][1001]={0}; // 그래프
int visit[1001]={0}; // 방문처리 배열, visit[v] == true : v는 방문했다.
int q[1001]; // 큐 자료구조
// BFS 함수 정의
void BFS(int v,int N){
// front : pop 노드 가리키는 인덱스
// rear : 큐의 끝
// pop : 큐에서 꺼낸 노드
int front=0, rear=0, pop, i;
printf("%d ",v);
q[front]=v; // 큐에 시작 노드 v 삽입
rear++; // 큐의 끝 수정
visit[v]=1; // 노드 방문처리
// 큐가 빌 때까지
while(front<rear){
pop=q[front];
front++;
for(i=1;i<=N;i++){
if(graph[pop][i]==1 && visit[i]==0){
printf("%d ",i);
q[rear]=i;
rear++;
visit[i]=1;
}
}
}
return;
}
int main(){
int N,M,Start;
int i,x,y;
scanf("%d%d%d",&N,&M,&Start);
for(i=0;i<M;i++){
scanf("%d%d",&x,&y);
graph[x][y]=1;
graph[y][x]=1;
}
BFS(Start,N);
return 0;
}
전형적인 그래프를 탐색하는데 사용했는데 1차원 배열이나 2차원 배열 또한 그래프 형태로 생각하면 수월하게 문제를 풀 수 있다. 특히 DFS 또는 BFS 문제 유형이 그러하다.
예를 들어 게임 맵이 3 x 3 형태의 2차원 배열이고 각 데이터를 좌표라고 생각해보자. 게임 캐릭터가 (1, 1) 좌표에 있다고 표현할 때처럼 말이다. 이때 각 좌표를 상하좌우로만 이동할 수 있다면 아래와 같이 생각할 수 있다.
2차원 배열에서의 탐색 문제를 위와 같은 그래프 형태로 바꿔서 생각하면 그래프 탐색을 통해서 문제를 풀 수 있다.
1. 연결 요소의 개수