BFS & DFS
- BFS, DFS는 BFT(Breadth-first traversal), DFT(Depth-first traversal)이라고도 불리는데,
traversal
은 트리에서 배웠던 순회와 같은 용어다.- 순회는 트리와 그래프 같은 연결 구조에서 노드를 방문하는데 사용된다.
- BFS와 DFS는 순회(방문)하면서 탐색하는 탐색 알고리즘이다.
- 단, 출발지 노드와 그래프/트리 구조에 따라 탐색하는 순서와 노드가 달라질 수 있다.
BFS(Breadth-first Search): 너비 우선 탐색
- 위 그림을 봤을 때, BFS는 큐의 개념이 사용된다.
- 노드 ‘S’ 부터 저장되고 사용된다.(FIFO)
- 순서: S-1-2-3-4-5-6-7
BFS의 활용
- 길 찾기, 라우팅
- BitTorrent와 같은 P2P 네트워크에서 인접 노드 찾기
- 웹 크롤러
- 소셜 네트워크에서 멀리 떨어진 사람 찾기
- 그래프에서 주변 위치 찾기
- 네트워크에서 방송
- 그래프에서 주기 감지
- 연결된 구성 요소 찾기
- 몇 가지 이론적 그래프 문제 풀기
BFS 슈도 코드
1 |
|
슈도 코드 해석
- 그래프의 각 노드를 보고 방문할 노드에 대해 구분한다.
- 일단 모든 verts(정점 또는 노드)를 방문하지 않은 상태(white)이다.
- 다음으로 시작 노드를 gray로 표시한다.
- gray는 시작 노드의 이웃을 탐색한다.
- 시작 노드를 큐에 넣는다.
- 이는 while 루프에 들어가면 첫 번째 vert가 될 것임을 의미한다.
- while 루프의 시작 부분에서 확인하는 조건은, 큐가 비어 있지 않은지 여부이다.
- 비어 있지 않으면 큐의 첫 번째 항목을 변수에 저장한다.(u = queue[0])
- 각 이웃의 vert에 대해 반복문을 수행한다.
- 방문하지 않았는지(white) 확인한다.
- 방문하지 않은 경우 gray로 표시한다.(이웃을 탐색한다는 의미이다.)
- 탐색한 현재 vert를 대기열에서 빼고(deque), 해당 vert를 black으로 표시한다.(방문한 것으로 표시)
- 그래프의 모든 verts를 탐색할 때까지 위의 프로세스를 계속 진행
- BFS는 큐 자료 구조를 활용하고 재귀 호출은 하지 않는다.
- BFS는 노드가 적은 경우 최단 경로를 탐색할 때 유용하다.
- 너비 우선적으로 노드를 탐색하는 경우, 큐를 활용하므로 노드가 많아지는 경우 메모리 저장 공간이 증가하는 단점이 있다.
- BFS는 재귀적으로 동작하지 않는데, 인접한 노드에 대해 먼저 탐색하기 때문에 재귀적으로 재호출할 필요가 없다.
- DFS와 개념을 비교해보면서 자세히 살펴보자.
DFS(Depth-first Search): 깊이 우선 탐색
- 깊이 우선 탐색은 LIFO로써 스택의 개념을 사용한다.
- 순서: 1-2-4-5-3
- 깊이 우선 탐색 알고리즘은 다른 경로를 역추적하고 탐색하기 전에 가능한 그래프를 분할한다.
- 깊이 별로 탐색을 진행하기 때문에, 내부적으로 그래프가 분할된 후 탐색을 진행한다는 것이다.
- 위 그림에서 1-2-4-5 까지 탐색을 지행하고, 분할이 진행된 후 3을 탐색한다.
- 백트래킹은 DFS에서 활용되는 방법인데, 쉽게 말해 노드가 있을만한 곳을 되돌아가서 모두 살펴본다는 개념이다.
- 위의 그림처럼 DFS는 깊이 우선적으로 탐색을 진행하고, 재귀적으로 아래에서부터 탐색하지 않은 정점이 있는 지 확인하는 방법이다.
DFS의 활용
- 가중 그래프의 최소 스패닝 트리 찾기
- 길 찾기
- 그래프에서주기 감지
-
미로 문제
- DFS는 그래프의 모든 노드를 방문하는 경우 사용된다.
- DFS의 단점은 최단 경로를 찾지 못하고, 시간이 오래 걸릴 수 있다.
DFS 슈도 코드
1 |
|
- 위 슈도 코드에는 두 가지 기능이 있다.
- 첫 번째 함수인 DFS()는 그래프를 매개 변수로 사용하고 모든 verts를 방문하지 않음(white)으로 표시한다.
- 각 vert의 부모를 null로 설정한다.
- 이 함수의 다음 루프는 그래프의 각 vert를 방문한다.
- 방문하지 않은 경우, 해당 vert를 두 번째 함수 DFS_visit()에 전달한다.
- 두 번째 함수인 DFS_visit()는 vert를 gray로 표시하여 시작한다.(탐색 과정에서)
- 그런 다음 방문하지 않은 모든 노드(인접 노드)의 갯수만큼 반복문을 수행한다.
- 이 루프에서 부모를 설정한 다음 DFS_visit()를 재귀적으로 호출한다.
- 모든 이웃 탐색이 완료되면 vert를 black(방문함)으로 표시한다.
BFS와 DFS의 중간 비교
DFS 장점
- 찾는 노드의 depth가 깊을수록 빠르다.
- 메모리를 적게 차지한다.
BFS 장점
- 최단 경로를 찾기 적합하다.
- 찾는 노드가 인접한 경우 효과적이다.
DFS 단점
- 노드가 무한한 갯수로 존재하는 경우, 무한반복에 빠진다.
BFS 단점
- 큐를 이용해 노드를 저장하기 때문에 노드의 수가 많을수록 메모리를 많이 소비한다.
BFS & DFS 파이썬 소스 코드
BFS
1 |
|
DFS
1 |
|
재귀를 이용한 DFS 구현
1 |
|