그래프 탐색이란, 하나의 시작점 노드에서 연결된 노드들을 모두 찾는 것을 의미한다.
시작점 노드는 정하기 나름이며 이 시작점으로부터 다른 노드들을 찾아가는 것이다.
Breadth First Search
와 Depth First Search
로 나눈다.Breadth는 너비를 뜻한다. 쉽게 생각해서 높이(혹은 깊이)와 반대되는 표현이라고 생각할 수 있다. BFS는 따라서 너비를 우선적으로 탐색하는 것을 의미한다.
그래프에서 너비를 우선으로 탐색하겠다는 게 무슨 말일까? 아래 예시를 봐보자.
큐는 BFS 알고리즘에서 굉장히 중요한 역할을 한다. 큐를 간단히 알아보자.
큐의 기능
파이썬에서 큐 사용하기
from collections import deque
queue = deque()
# 큐 맨 끝에 뒤에 데이터 추가
queue.append("태호")
queue.append("영훈")
queue.append("현승")
queue.append("지웅")
# 큐 데이터를 출력한다
print(queue) # 태호, 영훈, 현승, 지웅
# 큐 마지막 데이터를 삭제한다
# popleft 메소드를 사용하면 가장 앞에 있는 데이터를 삭제한다.
# 보통 큐에서도 마지막 데이터를 삭제하면 해당 메소드가 삭제한 데이터를 리턴한다
print(queue.popleft()) # 태호
print(queue.popleft()) # 영훈
print(queue.popleft()) # 현승
# 큐 데이터를 출력한다
print(queue) # 지웅
아래 예시를 보면서 하나하나 BFS 과정을 알아보자! 중요한 점이 이미 방문한 노드들과 그렇지 않은 노드들을 구별해야 한다.
너비
우선적으로 탐색된다.정리해서 일반화하면 아래와 같다.
- BFS 알고리즘
- 시작 노드를 방문 표시후, 큐에 넣는다
- 큐에 아무 노드가 없을 때까지 :
- 큐 가장 앞 노드를 꺼낸다
- 꺼낸 노드에 인접한 노드들을 모두 보면서 :
- 처음 방문한 노드면 :
- 방문한 노드 표시를 해준다
- 큐에 넣어준다
visited 변수로 방문을 확인한다. visited가 False면 방문하지 않은 역, Tru면 이미 방문한 노드이다.
from collections import deque
from subway_graph import create_station_graph
def bfs(graph, start_node):
"""시작 노드에서 bfs를 실행하는 함수"""
visited = []
queue = deque() # 빈 큐 생성
queue.append(start_node) # 처음에는 시작 노드 담아주고 시작하기.
# 일단 모든 노드를 방문하지 않은 노드로 표시
for station_node in graph.values():
station_node.visited = False
while queue: # 더 이상 방문할 노드가 없을 때까지.
node = queue.popleft()
node.visited = True
for station in node.adjacent_stations:
if station.visited is False:
queue.append(station)
stations = create_station_graph("./new_stations.txt") # stations.txt 파일로 그래프를 만든다
gangnam_station = stations["강남"]
# 강남역과 경로를 통해 연결된 모든 노드를 탐색
bfs(stations, gangnam_station)
# 강남역과 서울 지하철 역들이 연결됐는지 확인
print(stations["강동구청"].visited)
print(stations["평촌"].visited)
print(stations["송도"].visited)
print(stations["개화산"].visited)
# 강남역과 대전 지하철 역들이 연결됐는지 확인
print(stations["반석"].visited)
print(stations["지족"].visited)
print(stations["노은"].visited)
print(stations["(대전)신흥"].visited)
DFS(Depth First Search)는 말그대로 깊이를 우선적으로 보고 탐색하는 것을 의미한다.
BFS와는 다르게 하나의 노드에서 최대한 깊이, 그리고 멀리 가는 탐색 방법이다.
DFS에서의 중요한 추상자료형은 바로 스택이다.
스택은 이런 기능을 약속하는 추상자료형으로, 가장 마지막에 들어간 데이터가 가장 먼저 나오기 때문에 이것을 LIFO(Last-In-First-Out)이라고 부른다.
from collections import deque
stack = deque()
# 스택 뒤에 정보 추가: "Taeho!"
stack.append("T")
stack.append("a")
stack.append("e")
stack.append("h")
stack.append("o")
# 스택 정보를 출력한다
print(stack) # T, a, e, h, o
# 스택 마지막 정보를 삭제한다
# 파이썬 deque에서는 pop 메소드를 이용하면 뒤에서부터 데이터를 삭제할 수 있ek.
# 보통 스택에서 마지막 데이터를 삭제하면 해당 메소드가 삭제한 데이터를 리턴한다
print(stack.pop()) # o
print(stack.pop()) # h
print(stack.pop()) # e
# 스택 정보를 출력한다
print(stack) # T, a
아래 예시를 보면서 하나하나 BFS 과정을 알아보자! 중요한 점이 BFS와 마찬가지로 이미 방문한 노드들과 그렇지 않은 노드들을 구별해야 한다.
깊이
를 우선으로 한 탐색이 끝난다. 정리해서 일반화하면 아래와 같다.
- DFS 알고리즘
- 시작 노드를 회색 표시후, 스택에 넣는다
- 스택에 아무 노드가 없을 때까지 :
- 스택 가장 위 노드를 꺼낸다
- 노드를 방문(진한 회색) 표시한다.
- 인접한 노드들을 모두 보면서 :
- 처음 방문하거나 스택에 없는 노드면 :
- 옅은 회색 표시를 해준다
- 스택에 넣어준다