๐ง ๊น์ด ์ฐ์ ํ์ ( Depth First Search )
๐ค ๊น์ด์ฐ์ ํ์, DFS๋?
๊น์ด ์ฐ์ ํ์, ์ฆ DFS๋ ๊ทธ๋ํ์ ์์ ํ์(Exhaustive Search)๊ธฐ๋ฒ ์ค ํ๋
- ๊ทธ๋ํ์ ์์๋ ธ๋์์ ์ถ๋ฐํด ํ์ํ ํ์ชฝ ๋ถ๊ธฐ๋ฅผ ์ ํ๋ค.
- ์ต๋ ๊น์ด๊น์ง ํ์์ ๋ง์น๋ฉด ๋ค๋ฅธ ๋ถ๊ธฐ๋ก ์ด๋ํด ๋ค์ ํ์์ ์งํํ๋ ์๊ณ ๋ฆฌ์ฆ์ด๋ค.
DFS๋ ๋ค์๊ณผ ๊ฐ์ ํน์ง์ด ์๋ค.
DFS๋ ์ฌ๊ทํจ์์ ์ด์ฉ์ผ๋ก stack overflowํ์์ ๋ํด ์ฃผ์ํด์ผ ํ๋ค.
๊ธฐ๋ฅ ํน์ง ์๊ฐ๋ณต์ก๋ G(V, E) ๊ทธ๋ํ์ ์์ ํ์ (Exhaustive Search) ์ฌ๊ทํจ์๋ ์คํ ์๋ฃ๊ตฌ์กฐ๋ก ๊ตฌํ O(V+E)
๐ค DFS๋ฅผ ์์ฉํด ํ ์ ์๋ ๋ฌธ์
- ๋ฐฑํธ๋ํน(Backtracking) [https://www.acmicpc.net/step/34]
- ๋จ์ ์ (Articulation Point) ์ฐพ๊ธฐ [https://www.acmicpc.net/problem/11266]
- ๋จ์ ์ ์ฐพ๊ธฐ [https://www.acmicpc.net/problem/11400]
- ์ฌ์ดํด ์ฐพ๊ธฐ
- ์์ ์ ๋ ฌ(topological sort) [https://www.acmicpc.net/step/25]
๐ค DFS์ ํต์ฌ ์ด๋ก
DFS๋ ํ ๋ฒ ๋ฐฉ๋ฌธํ ๋ ธ๋๋ฅผ ๋ค์ ๋ฐฉ๋ฌธํ๋ฉด ์๋๋ค.
๋ฐ๋ผ์ ๋ ธ๋ ๋ฐฉ๋ฌธ์ฌ๋ถ๋ฅผ ์ฒดํฌํ ๋ฆฌ์คํธ๊ฐ ํ์ํ๋ค.
DFS ํ์๋ฐฉ์์ LIFOํน์ฑ์ ์คํ์ ์ฌ์ฉํด ์ค๋ช ํ์๋ฉด, ์๋์ ๊ฐ๋ค.
1. DFS๋ฅผ ์์ํ ๋ ธ๋๋ฅผ ์ ํ ํ ์ฌ์ฉํ ์๋ฃ๊ตฌ์กฐ ์ด๊ธฐํ
- DFS๋ฅผ ์ํด ํ์ํ ์ด๊ธฐ ์์ ์๋์ ๊ฐ๋ค.
โฃ ์ธ์ ๋ฆฌ์คํธ๋ก ๊ทธ๋ํ๋ฅผ ํํ
โฃ ๋ฐฉ๋ฌธ๋ฆฌ์คํธ๋ฅผ ์ด๊ธฐํํ๊ธฐ
โฃ ์์ ๋ ธ๋ ์คํ์ ์ฝ์ ํ๊ธฐ
- ์คํ์ ์์๋ ธ๋๋ฅผ 1๋ก ์ฝ์ ํ ๋, ํด๋น ์์น์ ๋ฐฉ๋ฌธ๋ฆฌ์คํธ๋ฅผ ์ฒดํฌํ๋ฉด T, F, F, F, F, F ์ด๋ค.
2. ์คํ์์ ๋ ธ๋๋ฅผ ๊บผ๋ด ์ธ์ ๋ ธ๋๋ฅผ ๋ค์ ์คํ์ ์ฝ์
- ์ด์ pop์ ์ํํ์ฌ ๋ ธ๋๋ฅผ ๊บผ๋ด๊ณ ๊บผ๋ธ ๋ ธ๋๋ฅผ ํ์์์์ ๊ธฐ๋กํ๋ค.
- ์ธ์ ๋ฆฌ์คํธ์ ์ธ์ ๋ ธ๋๋ฅผ ์คํ์ ์ฝ์ ํ๋ฉฐ ๋ฐฉ๋ฌธ๋ฆฌ์คํธ๋ฅผ ์ฒดํฌํ๋ค.
- ์ด๋, ๋ฐฉ๋ฌธ๋ฆฌ์คํธ๋ T, T, T, F, F, F ์ด๋ค.
3. ์คํ์ ๊ฐ์ด ์์ ๋๊น์ง ๋ฐ๋ณต
- ์์ ๊ณผ์ ์ ์คํ์๋ฃ๊ตฌ์กฐ์ ๊ฐ์ด ์์ ๋๊น์ง ๋ฐ๋ณตํ๋ฉฐ ์ด๋ฏธ ๊ฑฐ์น ๋ ธ๋๋ ์ฌ์ฝ์ ํ์ง ์๋ ๊ฒ์ด ํต์ฌ!
๐ค DFS ์๋์ฝ๋
DFS์ ์ฌ๊ท ๊ตฌํ
DFS๋ฅผ ์ฌ๊ท๋ก ๊ตฌํํ๋ ๊ฒฝ์ฐ์ ์๋ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
์ฝ๊ฒ ๋งํ์๋ฉด ์ ์ ์ ๋ฐฉ๋ฌธํ๋ ๋ฐฉ๋ฌธ๋์๋ค๊ณ ํ์ํ๊ณ ํด๋น ์ ์ ์ ์ธ์ ๊ฐ์ ์ ๋ชจ๋ ํ์ํ๋ ๊ฒ.
์ด๋, ํด๋น ์ ์ ์ด ๋ฐฉ๋ฌธ๋์ง ์์๋ค๋ฉด ํด๋น ์ ์ ์ ๋ํด์ ๋ค์ DFS๋ฅผ callํ๋ ํํ
์ ์ ์ ์ธ์ ๊ฐ์ ์ ํ๋์ฉ ํ์ ํ ํ์์ด ๋ถ๊ฐ๋ฅํ๋ฉด ๋น ์ ธ๋์ ๋ค๋ฅธ ์ธ์ ๊ฐ์ ์ ์ด์ ๋์ผํ ๋ฐฉ์์ผ๋ก ํ์์งํ
<pseudocode>
DFS(G, v)
visit v;
mark v as visited;
for all directed edges from v to w that are in G.adjacentEdges(v) do
if vertex w is not marked as visited then
recursively call DFS(G, w)
<python code>
def recursive_dfs(v, visited=[]):
visited.append(v)
for w in graph[v]:
if not w in visited:
visited = recursive_dfs(w, visited)
return visited
์ด๋, graph๋ global ๋ณ์๋ก ์์ฉํ๊ฒ ๋๊ณ , visited๋ ํจ์์ ์ธ์๋ก ๊ณ์ ๋ฃ์ด์ฃผ๋ ํํ์ด๋ค.
์ด๋ก ์ธํด visited๋ฅผ ๋ฆฌํดํด์ ๊ณ์ ๋์ ํด์ฃผ๋ ๋ฐฉ์์ผ๋ก ์งํ์ ํด์ค์ผํ๋ค.
DFS์ ์คํ ๊ตฌํ
์คํ์ ์ด์ฉํ๋ ๊ฒฝ์ฐ์๋ ๋ฐ๋ณต๋ฌธ์ ์ด์ฉํด ๊ตฌํํ ์ ์๋ค.
์คํ์ ์ด์ฉํด ๋ชจ๋ ์ธ์ ๊ฐ์ ์ ์ถ์ถํ๊ณ ๋ค์ ๋์ฐฉ์ ์ธ ์ ์ ์ ์คํ์ ์ฝ์ ํ๋ ๊ตฌ์กฐ๋ก ๊ตฌํ์ด ๊ฐ๋ฅํ๋ค.
<pseudocode>
DFS_iterative(G,v)
let S be a stack
S.push(v)
while S is not empty do
v = S.pop()
if v is not marked as visited then
mark v as visited
for all edges from v to w in G.adjacentEdges(v) do
S.push(w)
์คํ์ ํ ๋ฒ์ ๋ชจ๋ ์ธ์ ๊ฐ์ ์ ์ ์ ์ ๋ค pushํ๊ธฐ ๋๋ฌธ์ ์์นซ BFS๋ก ์คํดํ๊ธฐ ์ฝ๋ค.
์ฌ๊ธฐ์ ์ผ๋ํด์ผํ ์ ์ stack์ push๋ ๋๊ฐ ์๋๋ผ pop์ด ๋๋ฉด์ ๋ฐฉ๋ฌธ์ checkํ๋ ๊ตฌ์กฐ์ด๋ค.
๋ฐ๋ผ์ ์ ์ ๋ฐฉ๋ฌธ ์์๋ก ๋ณธ๋ค๋ฉด ํ์คํ DFS์์ ์ ์ ์๋ค.
<python code>
def iterative_dfs(start_v):
visited = []
stack = [start_v]
while stack:
v = stack.pop()
if v not in visited:
visited.append(v)
for w in graph[v]:
stack.append(w)
return visited
ํจ์จ์ฑ
- ์๊ฐ ๋ณต์ก๋
์ธ์ ํ๋ ฌ์ ๊ฒฝ์ฐ ํ๋์ ์ ์ ๋น n๋ฒ check๋ฅผ ํด์ฃผ์ด์ผ ํ๊ธฐ ๋๋ฌธ์ O(n^2)์ด๋ค.
์ธ์ ๋ฆฌ์คํธ์ ๊ฒฝ์ฐ ์ ์ ๋ฐฉ๋ฌธ & ํด๋น ์ ์ ์ ์ธ์ ์ ์ ๋ฐฉ๋ฌธ์ด๋ฏ๋ก O(n+m)์ด๋ค.
์๊ฐ ๋ณต์ก๋์ ๊ฒฝ์ฐ์๋ ๊ฒฐ๊ตญ์ ์ด๋ค ์ ์ ์ ํ์ํ๋๋์ ๋ฌธ์ ์ด๊ธฐ ๋๋ฌธ์ ๊ณต๊ฐ ๋ณต์ก๋์ ๋์ผํ๋ค.
- ๊ณต๊ฐ ๋ณต์ก๋
๊ณต๊ฐ ๋ณต์ก๋์ ๊ฒฝ์ฐ, ๊ทธ๋ํ์ visited, ์คํ์ ๋ชจ๋ ๊ณ ๋ คํด์ฃผ์ด์ผ ํ๊ธฐ์ ๊ทธ๋ํ์ ๊ณต๊ฐ๋ณต์ก๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
์ธ์ ํ๋ ฌ์ ๊ฒฝ์ฐ์๋ ํ๋์ ์ ์ ๊ณผ ๋ชจ๋ ์ ์ ๊ฐ์ ์ฐ๊ฒฐ ๊ด๊ณ๋ฅผ ๋ค ํํํด์ฃผ์ด์ผ ํ๊ธฐ ๋๋ฌธ์ O(n^2)์ด๋ค.
์ธ์ ๋ฆฌ์คํธ์ ๊ฒฝ์ฐ์๋ ๊ทธ๋ํ ๋ด ์ ์ ๊ฐ์๋ฅผ n, ๊ฐ์ ๊ฐ์๋ฅผ m์ด๋ผ๊ณ ํ ๋ O(n+m)์ด๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก ๊ณต๊ฐ๋ณต์ก๋๋ ๊ทธ๋ํ์ ๊ณต๊ฐ๋ณต์ก๋๋ฅผ ๋ฐ๋ผ ๊ฐ๊ฒ ๋๋ค.
DFS์ ํจ์จ์ฑ์ ๋ํด ๋งํ ๊ฒฝ์ฐ, ์คํ๊ณผ visited๋ ๋ชจ๋ O(n)์ ๊ณต๊ฐ๋ณต์ก๋๋ฅผ ๊ฐ์ง๊ณ ์๋ค (์ ์ ๊ฐ์ n).
์ด๋ ๊ทธ๋ํ๊ฐ ์ธ์ ํ๋ ฌ์ด๋ ์ธ์ ๋ฆฌ์คํธ๋ก ๊ตฌํ๋์ด ์๋ ๋ ๊ฐ์ง ๊ฒฝ์ฐ ๋ชจ๋์ ํด๋นํ๋ค.
๐ง ๋ฐฑ์ค 11724 (Connected Component)
- connected component ์ฆ, ์ฐ๊ฒฐ์์๋ ๋ค์๊ณผ ๊ฐ์ ์กฐ๊ฑด์์ ์ฑ๋ฆฝํ๋ค.
- ์ฐ๊ฒฐ์์์ ์ํ ๋ชจ๋ ์ ์ (vertex)์ ์ฐ๊ฒฐํ๋ ๊ฐ์ (edge)๊ฐ ์์ด์ผ ํ๋ค.
- ๋ ๋ค๋ฅธ ์ฐ๊ฒฐ์์์ ์ํ ์ ์ (vertex)๊ณผ ์ฐ๊ฒฐํ๋ ๊ฐ์ (edge)์ด ์์ผ๋ฉด ์๋๋ค.
๐ค Algorithm ๊ณผ์
0. ๋ ธ๋์ ์ต๋ ๊ฐ์๊ฐ 1000์ด๋ฏ๋ก ์๊ฐ๋ณต์ก๋ n^2์ดํ์ ์๊ณ ๋ฆฌ์ฆ์ ๋ชจ๋ ์ฌ์ฉํ ์ ์๋ค.
1. ๊ทธ๋ํ๋ฅผ ์ธ์ ๋ฆฌ์คํธ๋ก ์ ์ฅํ๋ค. (๋ฌด๋ฐฉํฅ ๊ทธ๋ํ์ด๊ธฐ์ ์์ชฝ๋ฐฉํฅ์ผ๋ก ๊ฐ์ ์ ๋ชจ๋ ์ ์ฅํ๋ค.)
2. ๋ฐฉ๋ฌธ๋ฆฌ์คํธ๋ฅผ ๋ชจ๋ False๋ก ์ด๊ธฐํํ๋ค.
3. DFS๋ฅผ ์ํํ๋๋ฐ, ์๋์ ๊ฐ์ด ์งํํ๋ค.
๐คซ solution_11724import sys sys.setrecursionlimit(10000) input = sys.stdin.readline V, E = map(int, input().split()) CC = [[] for _ in range(V+1)] # [[], [], [], [], [], [], []] visit_list = [False] * (V+1) for _ in range(E): u, v = map(int, input().split()) # ์๋ฐฉํฅ ๊ฐ์ ์ด๋ฏ๋ก ์์ชฝ์ ์ ์ ๋ํ๊ธฐ. CC[u].append(v) CC[v].append(u) # print(CC) => [[], [2, 5], [1, 5], [4], [3, 6], [2, 1], [4]] # ์ธ์ ๋ฆฌ์คํธ ์์ฑ [1] [2] [3] [4] [5] [6] def DFS(v): visit_list[v] = True for i in CC[v]: if not visit_list[i]: # ์ฐ๊ฒฐ์์๊ฐ ๋ฐฉ๋ฌธ๋ฆฌ์คํธ์ F๋ก ๋์ด์์ผ๋ฉด True๋ก ๊ณ ์น๊ธฐ ์ํ ์ฌ๊ท DFS(i) cnt = 0 for i in range(1, V+1): if not visit_list[i]: # 1 2 3 4 5 6์ ๋ฐฉ๋ฌธ๋ฆฌ์คํธ์ ๋ํด ๋ฐฉ๋ฌธํ์ ์ด ์๋ค๋ฉด cnt += 1 DFS(i) print(cnt)