그래프 탐색
DFS
막다른 길까지 가 보기
왼쪽 위 시작 칸에서 오른쪽 아래 도착 칸까지 미로를 걸어 보세요. 옆 칸을 눌러 한 칸씩 갑니다. 막다른 길에서는 되돌아가기를 눌러 지나온 길로 돌아옵니다.
미로를 걸어 도착 칸에 닿으세요. 먼저 한 가지만 골라 주세요.
★ 갈림길에서 오른쪽 길로 끝까지 가면 어떻게 될까요?
★ 는 길이 둘로 갈라지는 칸이에요.
걸음 0 · 되돌아가기 0번 · 지금 길 0걸음
갈림길에서 한 길을 골라 막다른 길이나 도착이 나올 때까지 끝까지 가 보고, 막히면 돌아와 다음 길로 가는 방법 = 깊이 우선 탐색(DFS, Depth-First Search)
- 오른쪽 길 — 위로 돌아서 9걸음에 도착한다
- 아래 길 — 곧장 7걸음에 도착한다
- 돌아올 길을 기억하는 곳 — 나중에 넣은 것이 먼저 나오는 더미, 스택이다. 함수가 자기를 부르는 재귀도 같은 일을 한다
같은 미로에서 가까운 칸부터 퍼지는 BFS 는 7걸음 최단을 찾습니다. 한 길을 끝까지 가는 DFS 는 오른쪽 길로 가서 9걸음에 닿습니다.
지나온 칸에 표시하지 않으면 같은 곳을 맴돈다
- 이미 온 칸은 못 들어간다 — 미로에서 눌러 보면 막히던 규칙이다
- 표시가 없으면 — 같은 칸을 몇 번이고 다시 넣는다. 아래 코드의
dist가 그 표시다 - 그래서 — 표시를 뺀 코드는 이 미로에서 1,000번을 꺼내도 도착 칸에 닿지 못했다
스택이 되돌아올 길을 기억한다
스택 = 나중에 넣은 게 먼저 나오는 더미. 위에서 걸은 것과 같은 미로입니다. 격자 BFS 코드에서 popleft() 를 pop() 으로 바꾼 코드입니다.
다음을 눌러 한 줄씩 넘기며 스택 맨 위(오른쪽 끝)가 먼저 나오는 걸 보세요. 숫자는 그 칸까지의 걸음 수입니다.
시작★도착
스택바닥
- 0
다음을 눌러 한 줄씩 넘겨 보세요
스택 = 1
dfs.py
- 1def dfs(grid, start, goal):
- 2 rows, cols = len(grid), len(grid[0])
- 3 stack = [start]
- 4 dist = {start: 0}
- 5 while stack:
- 6 r, c = stack.pop()
- 7 if (r, c) == goal:
- 8 return dist[(r, c)]
- 9 for dr, dc in ((-1,0),(1,0),(0,-1),(0,1)):
- 10 nr, nc = r + dr, c + dc
- 11 if not (0 <= nr < rows and 0 <= nc < cols):
- 12 continue
- 13 if grid[nr][nc] == 0 and (nr, nc) not in dist:
- 14 dist[(nr, nc)] = dist[(r, c)] + 1
- 15 stack.append((nr, nc))
- 16 return -1
0 / 31
pop()— 스택 맨 위, 가장 나중에 넣은 칸을 꺼낸다. 막다른 길이면 그 아래 칸이 맨 위가 되어 되돌아온 것이다stack.append— 이웃을 쌓는다. 마지막에 쌓은 칸이 가장 먼저 나온다not in dist— 이미 넣은 칸은 다시 쌓지 않는다
이 미로에서는 아래 줄 끝의 막다른 칸까지 파고든 뒤에야 도착 칸이 나옵니다. 꺼낸 횟수는 15번입니다.
아래 길의 한 칸을 벽으로 막으면, 같은 코드가 도착까지 낸 걸음 수는 몇 걸음일까요?
DFS 에서 틀리는 자리
- 방문 표시를 빼면 — 이미 온 칸을 계속 다시 쌓아 끝나지 않는다. 위 미로에서 1,000번을 꺼내도 도착하지 못했다
- 재귀로 쓰면 — 길이 길수록 깊이가 칸 수만큼 는다. 파이썬 기본 한도는 1000이라 벽 없는 32×32 격자(1,024칸)에서
RecursionError가 난다. 31×31 은 된다 - 최단 거리를 보장하지 않는다 — 같은 미로에서 BFS 는 7걸음, 이 코드는 9걸음이다
- 이웃 쌓는 순서 — 순서를 거꾸로 바꾸면 같은 미로에서 9걸음이 7걸음으로 바뀐다. 길이 둘이면 먼저 가는 쪽이 결과를 정한다
그래서 최단 거리가 필요하면 BFS 를, 길을 끝까지 다 살펴야 하면 DFS 를 씁니다.
DFS 를 꺼내라는 세 가지 신호
모든 경로를 찾는다도달할 수 있는가한 덩어리로 이어진 곳을 찾는다
다음 장에서는 이어진 덩어리가 몇 개인지 세는 연결 요소를 봅니다.