많은 양의 데이터 중에서 원하는 데이터를 찾는 과정
대표적인 그래프 탐색 알고리즘으로 DFS와 BFS가 있으며, 코딩 테스트에서 매우 자주 등장하므로 반드시 숙지해야 한다.
append() / pop()으로 구현한다.stack = []
stack.append(5)
stack.append(2)
stack.append(3)
stack.append(7)
stack.pop() # 7 제거
stack.append(1)
stack.append(4)
stack.pop() # 4 제거
print(stack[::-1]) # 최상단 원소부터 출력: [1, 3, 2, 5]
print(stack) # 최하단 원소부터 출력: [5, 2, 3, 1]
collections.deque를 사용한다. (리스트보다 효율적)from collections import deque
queue = deque()
queue.append(5)
queue.append(2)
queue.append(3)
queue.append(7)
queue.popleft() # 5 제거
queue.append(1)
queue.append(4)
queue.popleft() # 2 제거
print(queue) # 먼저 들어온 순서대로: deque([3, 7, 1, 4])
queue.reverse()
print(queue) # 나중에 들어온 원소부터: deque([4, 1, 7, 3])
자기 자신을 다시 호출하는 함수
종료 조건 없이 재귀를 호출하면 최대 재귀 깊이를 초과해 에러가 발생한다.
def recursive_function(i):
if i == 100: # 종료 조건
return
print(i, '번째 재귀함수에서', i+1, '번째 재귀함수를 호출합니다.')
recursive_function(i + 1)
print(i, '번째 재귀함수를 종료합니다.')
recursive_function(1)
# 반복문으로 구현
def factorial_iterative(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
# 재귀로 구현
def factorial_recursive(n):
if n <= 1:
return 1
return n * factorial_recursive(n - 1)
print('반복적으로 구현:', factorial_iterative(5)) # 120
print('재귀적으로 구현:', factorial_recursive(5)) # 120
A를 B로 나눈 나머지를 R이라 할 때, GCD(A, B) = GCD(B, R)
def gcd(a, b):
if a % b == 0:
return b
else:
return gcd(b, a % b)
print(gcd(192, 162)) # 6
그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘
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번 노드와 연결
[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
그래프에서 가까운 노드부터 우선적으로 탐색하는 알고리즘
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 | BFS |
|---|---|---|
| 자료구조 | 스택 / 재귀 | 큐 |
| 탐색 방식 | 깊이 우선 | 너비 우선 |
| 최단 거리 | ❌ 보장 안 됨 | ✅ 보장됨 (비용 동일 시) |
| 주요 활용 | 연결 요소 개수, 사이클 탐지 | 최단 경로, 레벨 탐색 |
N×M 얼음 틀에서 0은 구멍, 1은 칸막이다. 상하좌우로 연결된 0끼리 하나의 아이스크림이 된다. 총 아이스크림의 개수를 구하라.
0인 칸에서 DFS를 수행한다.0 칸을 1로 바꿔 재방문을 방지한다.def dfs(x, y):
# 범위를 벗어나면 종료
if x <= -1 or x >= n or y <= -1 or y >= m:
return False
if graph[x][y] == 0:
graph[x][y] = 1 # 방문 처리
dfs(x - 1, y)
dfs(x + 1, y)
dfs(x, y - 1)
dfs(x, y + 1)
return True
return False
n, m = map(int, input().split())
graph = []
for i in range(n):
graph.append(list(map(int, input())))
result = 0
for i in range(n):
for j in range(m):
if dfs(i, j) == True:
result += 1
print(result)
# 입력 예시:
# 4 5
# 00110
# 00011
# 11111
# 00000
# 출력: 3
graph[x][y] = 1로 방문을 표시해 별도의 visited 배열 없이도 동작한다.False를 반환하고 종료한다.N×M 미로에서 (1,1)에서 출발해 (N,M)까지 이동할 때, 최소 칸의 개수를 구하라. 0은 괴물(이동 불가), 1은 이동 가능한 칸이다.
from collections import deque
def bfs(x, y):
queue = deque()
queue.append((x, y))
while queue:
x, y = queue.popleft()
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
if nx < 0 or nx >= n or ny < 0 or ny >= m:
continue
if graph[nx][ny] == 0: # 벽이면 무시
continue
if graph[nx][ny] == 1: # 처음 방문하는 경우만 기록
graph[nx][ny] = graph[x][y] + 1
queue.append((nx, ny))
return graph[n - 1][m - 1]
n, m = map(int, input().split())
graph = []
for i in range(n):
graph.append(list(map(int, input())))
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
print(bfs(0, 0))
# 입력 예시:
# 3 3
# 110
# 010
# 011
# 출력: 5
graph[nx][ny] == 1 조건으로 처음 방문하는 칸만 거리를 갱신한다.graph[n-1][m-1]을 반환하면 자연스럽게 최단 거리가 나온다.| 문제 유형 | 알고리즘 | 이유 |
|---|---|---|
| 연결된 구역의 개수 | DFS | 연결 요소 탐색에 적합 |
| 최단 경로 / 최소 칸 | BFS | 가까운 노드부터 탐색해 최단 거리 보장 |