그래프 탐색 알고리즘에는 대표적으로 깊이 우선 탐색(DFS)과 너비 우선 탐색(BFS)이 있다. 이를 이해하기 위해 먼저 그래프의 기본 개념을 살펴보자.
그래프는 노드(Node, 정점) 와 간선(Edge) 으로 이루어진 자료구조다. 프로그래밍에서 그래프를 표현하는 방식은 크게 두 가지가 있으며, 코딩 테스트에서는 이 두 방식 모두 필요하다.
인접 행렬(Adjacency Matrix): 2차원 배열로 그래프의 연결 관계를 표현하는 방식
인접 리스트(Adjacency List): 리스트로 그래프의 연결 관계를 표현하는 방식
파이썬에서는 2차원 리스트를 사용하여 인접 행렬을 구현할 수 있다. 연결되지 않은 노드끼리는 무한대(INF)로 표현한다.
INF = 999999999
graph = [
[0, 7, 5],
[7, 0, INF],
[5, INF, 0]
]
각 노드에 연결된 노드를 리스트로 저장한다. 파이썬에서는 리스트의 append() 메서드를 활용하여 구현할 수 있다.
graph = [[] for _ in range(3)]
graph[0].append((1, 7))
graph[0].append((2, 5))
graph[1].append((0, 7))
graph[2].append((0, 5))
graph[a][b] 한 번만 조회하면 되지만, 인접 리스트는 노드 a의 연결 리스트를 순차적으로 확인해야 한다.DFS는 스택(Stack) 자료구조를 이용하여 구현할 수 있다. 또는 재귀 함수를 활용하여 간결하게 표현할 수도 있다.
def dfs(graph, v, visited):
visited[v] = True
print(v, end=' ')
for i in graph[v]:
if not visited[i]:
dfs(graph, i, visited)
graph = [
[],
[2, 3, 8],
[1, 7],
[1, 4, 5],
[3, 5],
[3, 4],
[7],
[2, 6, 8],
[1, 7]
]
visited = [False] * 9
dfs(graph, 1, visited)
1 2 7 6 8 3 4 5
BFS는 큐(Queue) 자료구조를 사용하며, deque 라이브러리를 활용하면 더욱 효율적으로 구현할 수 있다.
from collections import deque
def bfs(graph, start, visited):
queue = deque([start])
visited[start] = True
while queue:
v = queue.popleft()
print(v, end=' ')
for i in graph[v]:
if not visited[i]:
queue.append(i)
visited[i] = True
graph = [
[],
[2, 3, 8],
[1, 7],
[1, 4, 5],
[3, 5],
[3, 4],
[7],
[2, 6, 8],
[1, 7]
]
visited = [False] * 9
bfs(graph, 1, visited)
1 2 3 8 7 4 5 6
| 알고리즘 | 사용 자료구조 | 탐색 방식 | 시간 복잡도 |
|---|---|---|---|
| DFS | 스택 (재귀 사용 가능) | 깊이 우선 | O(N) |
| BFS | 큐 (deque 활용) | 너비 우선 | O(N) |
DFS와 BFS는 그래프 탐색의 기본이 되는 중요한 알고리즘이다. 각각의 특징과 구현 방식을 숙지하고, 문제 유형에 따라 적절한 알고리즘을 선택하는 것이 중요하다. 추가로, 코딩 테스트 중 2차원 배열에서의 탐색 문제를 만나면 그래프 형태로 바꿔서 생각하면 풀이 방법을 조금 더 쉽게 떠올릴 수 있다는 점을 알아두자.