그래프를 탐색하는 방법에는 두 가지가 있습니다.
하나는 너비를 우선으로 탐색하는 방법(breadth-first search), 다른 하나는 깊이를 우선으로 탐색하는 방법(deapth-first search)입니다.
📌여기서 그래프 탐색이란?
그래프를 탐색하는 순서에 따라서 BFS와 DFS를 구분할 수 있고, 두 알고리즘에 사용되는 자료구조와 구현 방식이 다릅니다. 한 번 자세히 알아봅시다👩🏫
BFS는 루트 노드(혹은 다른 임의의 노드)에서 시작하여 인접한 노드들을 우선으로 탐색하는 방법입니다.
즉, 현재 노드와 가까이 있는 노드들을 우선으로 탐색하고 멀리 있는 노드들은 나중에 탐색하는 것이죠!
시작 노드로부터 깊이가 1인 노드들을 먼저 탐색하고, 그 다음에 깊이가 2인 노드, 깊이가 3인 노드 등의 순서로 노드를 탐색하는 것으로도 이해할 수 있습니다.
두 노드 사이의 최단 경로 혹은 임의의 경로를 찾고 싶을 때 BFS를 많이 사용합니다.
👇BFS로 그래프 탐색 예시 (시작 노드가 3인 경우)
👇BFS로 트리 탐색 예시
💡 큐는 FIFO이기 때문에, 먼저 큐에 들어간 깊이가 얕은 노드들이 먼저 탐색이 됩니다. 따라서, BFS는 큐로 구현이 가능한 것입니다!
from collections import deque
def bfs(edge, n, v):
deq = deque([v]) # 시작 노드를 큐에 삽입
visited = [v] # 시작 노드를 방문 체트 리스트에 삽입
while deq: # 큐에 노드가 없으면 종료
current = deq.popleft() # 큐에서 노드 꺼내기
print(current, end=' ')
for i in range(1, n+1): # 노드들이 1번부터 n번까지 있다고 가정
if edge[current][i] == 1 and i not in visited:
deq.append(i)
visited.append(i)
edge
: 노드들의 연결 여부를 담고 있는 이차원 배열로, 1의 값을 가지면 해당 노드들이 연결되어 있음 n
: 노드의 개수v
: 시작 노드deq
: 노드들을 담을 큐를 표현하기 위해 collections
의 deque
사용visited
: 노드들의 재방문을 방지하기 위한 방문했던 노드들을 담아놓는 리스트current
: 현재 큐에서 꺼낸 노드DFS는 루트 노드(혹은 임의의 노드)에서 시작하여 다른 브런치로 넘어가기 전 해당 브런치의 모든 노드를 완벽히 탐색한 뒤, 다른 브런치로 넘어가 탐색하는 방법입니다.
즉, 현재 노드로부터 한 방향으로 끝까지 탐색하고 더 이상 갈 수 없게 되면 가장 가까운 갈림길로 되돌아와 다시 그 방향으로 탐색하는 것이죠!
BFS에 비해 간단하나, 단순 검색 속도 자체는 BFS에 비해 느립니다
모든 노드들을 탐색해야 하는 경우에 많이 사용합니다.
👇DFS로 그래프 탐색 예시 (시작 노드가 3인 경우)
👇DFS로 트리 탐색 예시
pop
되어 해당 노드와 연결되어 있는 노드들을 탐색한 노드는 다시 재방문하지 않도록 따로 체크해줍니다. def dfs_use_stack(edge, n, v):
deq = deque([v]) # 스택에 첫 번째 노드 삽입
visited = []
while deq: # 스택에 아무 노드가 없을 때까지 반복
current = deq.pop() # 스택에서 노드를 꺼냄
if current not in visited: # 아직 방문하지 않은 노드라면 연결된 노드 스택 삽입
print(current, end=' ')
visited.append(current)
for i in range(n, 0, -1): # 1
if edge[current][i] == 1:
deq.append(i)
#1 : 숫자가 작은 노드 우선으로 탐색하기 위해 큰 숫자의 노드부터 스택에 삽입하였습니다. 스택은 나중에 넣은 것이 먼저 나오기 때문에, 숫자가 작은 노드가 가장 마지막에 들어가면 큰 숫자의 노드보다 작은 숫자의 노드가 먼저 스택에서 나오면서 탐색됩니다.
def dfs_use_recursive(edge, visited, n, v):
print(v, end=' ')
for i in range(1, n+1):
if edge[v][i] == 1 and i not in visited: # 아직 탐색되지 않은 노드라면 재귀적으로 dfs 탐색
visited.append(i)
dfs_use_recursive(edge, visited, n, i)
edge
: 노드들의 연결 여부를 담고 있는 이차원 배열로, 1의 값을 가지면 해당 노드들이 연결되어 있음 n
: 노드의 개수v
: 시작 노드deq
: 노드들을 담을 스택을 표현하기 위해 collections
의 deque
사용visited
: 노드들의 재방문을 방지하기 위한 방문했던 노드들을 담아놓는 리스트current
: 현재 스택에서 꺼낸 노드for i in range(1, n+1)
) [알고리즘] 깊이 우선 탐색(DFS)이란
[알고리즘] 너비 우선 탐색(BFS)이란
썸네일 출처 : GIPHY