DFS는 깊이 우선 탐색으로, 그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘이다. DFS를 보기 전에 그래프(Graph)의 기본 구조를 알아보자.
그래프는 정점(node, vertex)과 간선(Edge)으로 표현된다. 그래프 탐색이란 하나의 노드를 시작으로 다수의 노드를 방문하는 것을 말한다. 또한 두 노드가 간선으로 연결되어 있다면 "두 노드는 인접하다(Adjacent)"라고 표현한다.
DFS는 탐색 알고리즘이다. 깊이 우선 탐색이므로 이 알고리즘은 특정한 경로로 최대한 깊숙이 들어가서 노드를 방문한 후, 다시 돌아가 다른 경로로 탐색하는 알고리즘이다. 구체적인 동작 과정은 다음과 같다.
방문처리는 스택에 한번 삽입되어 처리된 노드가 다시 삽입되지 않도록 체크하는 것을 의미한다. 방문 처리를 통해 각 노드를 한 번씩만 처리할 수 있다.
다음 그래프를 DFS 알고리즘으로 탐색해보자. 이때 탐색 시작 노드는 1번 노드이다. 인접 노드가 여러 개일 때는 일반적으로 번호가 낮은 것부터 우선한다.
step 1)
step 2)
step 3)
step 4)
step 5)
step 6)
step 7)
step 8)
step 9)
step 10)
step 11)
step 12)
step 13)
깊이 우선 탐색 알고리즘 DFS는 스택 자료구조에 기초한다. 실제로는 스택없이 재귀를 통해서 구현이 가능하다. 탐색을 수행함에 있어서 데이터의 개수가 N개인 경우 O(N)의 시간이 소요된다.
def dfs(v):
visited.add(v)
print(v, end=' ')
for i in graph[v]:
if i not in visited:
dfs(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};
void dfs(int v,int N){
int i;
visit[v]=1;
printf("%d ",v);
for(i=1;i<=N;i++){
if(graph[v][i]==1 && visit[i]==0){
dfs(i,N);
}
}
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;
}
dfs(start,N);
return 0;
}
DFS를 전형적인 그래프를 탐색하는데 사용했는데 1차원 배열이나 2차원 배열 또한 그래프 형태로 생각하면 수월하게 문제를 풀 수 있다. 특히 DFS 또는 BFS 문제 유형이 그러하다.
예를 들어 게임 맵이 3 x 3 형태의 2차원 배열이고 각 데이터를 좌표라고 생각해보자. 게임 캐릭터가 (1, 1) 좌표에 있다고 표현할 때처럼 말이다. 이때 각 좌표를 상하좌우로만 이동할 수 있다면 아래와 같이 생각할 수 있다.
2차원 배열에서의 탐색 문제를 위와 같은 그래프 형태로 바꿔서 생각하면 그래프 탐색을 통해서 문제를 풀 수 있다.
1. 단지번호 붙이기