2. DFS (Depth First Search)
우리말로는 깊이 우선 탐색
Void DFS(int i) {
visited[i] = true;
for(auto v : graph[i])
if(!visited[v])
DFS(v); // 방문하지 않았을 경우 재귀적으로 시행
}
쉽게 말해서 먼저 보이는 지점부터 방문한다!
한번 방문한 지점은 두번 다시 확인할 필요 없도록 true 로 마킹해둔다.
3. DFS 가 동작하는 과정
A
B C D
E F G H
J
I
1
2
3
4
5
6
7
8
9
10
방문순서 :
A - B - E - F - C - G - J - H - D - I
4. DFS 를 이용한 여러가지 알고리즘
● Flood fill
● Euler circuit
● Articulation point & Bridge
● Connected Components
● Strong Connected Components
● Topological Sort
● 2-SAT problem
● …...
5. #1 Connected Components
그래프 이론 용어 정리
● Connected component : 어떤 길이의 path 로든 서로 도달할 수 있는 정점들로
구성된 subgraph
○ C(v) = {u ∈ V | there exists a path in G from u to v }.
관계(Relation)의 관점으로 보면 reflecxive, symmetric, transitive 모두 성립.
Equivalence class 를 나타낼 때도 쓰일 수 있음.
9. #1 Connected Components
● Connected Component 는 어떻게 구할 것인가?
Undirected Graph 에서 도달할 수 있는 놈들끼리 분류한다.
● 그렇다면 어떻게 구현할 것인가?
방문한 적이 없는 각 정점마다 (더 이상 도달할 정점이 없을때까지) DFS를 수행한다.
10. #1 Connected Components
Void dfs(int u) {
visited[u] = true;
for(auto v : graph[u])
if(!visited[v])
dfs(u);
}
Int main() {
for(int i=1; i<=V; ++i)
if(!visited(i)) {
dfs(i); dfs_count++;
}
}
A
B
C
E
F
G
I
D
H
J
DFS 방문 순서 : (A)
* Connected component는 괄호로 묶어서 표시
11. #1 Connected Components
Void dfs(int u) {
visited[u] = true;
for(auto v : graph[u])
if(!visited[v])
dfs(u);
}
Int main() {
for(int i=1; i<=V; ++i)
if(!visited(i)) {
dfs(i); dfs_count++;
}
}
A
B
C
E
F
G
I
D
H
J
DFS 방문 순서 : (A B)
* Connected component는 괄호로 묶어서 표시
12. #1 Connected Components
Void dfs(int u) {
visited[u] = true;
for(auto v : graph[u])
if(!visited[v])
dfs(u);
}
Int main() {
for(int i=1; i<=V; ++i)
if(!visited(i)) {
dfs(i); dfs_count++;
}
}
A
B
C
E
F
G
I
D
H
J
DFS 방문 순서 : (A B C)
* Connected component는 괄호로 묶어서 표시
13. #1 Connected Components
Void dfs(int u) {
visited[u] = true;
for(auto v : graph[u])
if(!visited[v])
dfs(u);
}
Int main() {
for(int i=1; i<=V; ++i)
if(!visited(i)) {
dfs(i); dfs_count++;
}
}
A
B
C
E
F
G
I
D
H
J
DFS 방문 순서 : (A B C D)
* Connected component는 괄호로 묶어서 표시
14. #1 Connected Components
Void dfs(int u) {
visited[u] = true;
for(auto v : graph[u])
if(!visited[v])
dfs(u);
}
Int main() {
for(int i=1; i<=V; ++i)
if(!visited(i)) {
dfs(i); dfs_count++;
}
}
A
B
C
E
F
G
I
D
H
J
DFS 방문 순서 : (A B C D)
* Connected component는 괄호로 묶어서 표시
15. #1 Connected Components
Void dfs(int u) {
visited[u] = true;
for(auto v : graph[u])
if(!visited[v])
dfs(u);
}
Int main() {
for(int i=1; i<=V; ++i)
if(!visited(i)) {
dfs(i); dfs_count++;
}
}
A
B
C
E
F
G
I
D
H
J
DFS 방문 순서 : (A B C D E)
* Connected component는 괄호로 묶어서 표시
16. #1 Connected Components
Void dfs(int u) {
visited[u] = true;
for(auto v : graph[u])
if(!visited[v])
dfs(u);
}
Int main() {
for(int i=1; i<=V; ++i)
if(!visited(i)) {
dfs(i); dfs_count++;
}
}
A
B
C
E
F
G
I
D
H
J
DFS 방문 순서 : (A B C D E)
* Connected component는 괄호로 묶어서 표시
17. #1 Connected Components
Void dfs(int u) {
visited[u] = true;
for(auto v : graph[u])
if(!visited[v])
dfs(u);
}
Int main() {
for(int i=1; i<=V; ++i)
if(!visited(i)) {
dfs(i); dfs_count++;
}
}
A
B
C
E
F
G
I
D
H
J
DFS 방문 순서 : (A B C D E)
* Connected component는 괄호로 묶어서 표시
18. #1 Connected Components
Void dfs(int u) {
visited[u] = true;
for(auto v : graph[u])
if(!visited[v])
dfs(u);
}
Int main() {
for(int i=1; i<=V; ++i)
if(!visited(i)) {
dfs(i); dfs_count++;
}
}
A
B
C
E
F
G
I
D
H
J
DFS 방문 순서 : (A B C D E)
* Connected component는 괄호로 묶어서 표시
19. #1 Connected Components
Void dfs(int u) {
visited[u] = true;
for(auto v : graph[u])
if(!visited[v])
dfs(u);
}
Int main() {
for(int i=1; i<=V; ++i)
if(!visited(i)) {
dfs(i); dfs_count++;
}
}
A
B
C
E
F
G
I
D
H
J
DFS 방문 순서 : (A B C D E)
* Connected component는 괄호로 묶어서 표시
21. #1 Connected Components
Void dfs(int u) {
visited[u] = true;
for(auto v : graph[u])
if(!visited[v])
dfs(u);
}
Int main() {
for(int i=1; i<=V; ++i)
if(!visited(i)) {
dfs(i); dfs_count++;
}
}
A
B
C
E
F
G
I
D
H
J
DFS 방문 순서 : (A B C D E) (F G) (H I J)
* Connected component는 괄호로 묶어서 표시
22. #1 Connected Components
DFS 가 아니더라도, Disjoint Set 자료구조를 이용하여 구현할 수도 있다!
Disjoint Set 자료구조를 이용하는 방법은 Minimum Spanning Tree 세션에서 추후 설명
29. #3 Topological Sort
Void TopologicalSort(int u) {
Visited[u] = true;
for(auto v : graph[u])
if(!visited[v]) TopologicalSort(v); // 여기까지만 보면 DFS 와 유사함
stack_tmp.push(u); // 순회를 끝낸 노드는 push 해준다.
}
dependency가 없는 정점을 우선순위를 두더라도
구체적으로 어느 기준으로 정렬할 지 기준이 없으면 정답은 없음
30. Topological sort 관련 문제들
● 단순히 순서만 출력하는 문제
https://www.acmicpc.net/problem/1766
● 비용이 추가로 들어간 문제
https://www.acmicpc.net/problem/1005
https://www.acmicpc.net/problem/1516
https://www.acmicpc.net/problem/2056
31. #4 Strongly Connected Components
● C(v) = { u ∈ V | there exists a path in G from u to v and a path
in G from v to u }.
Directed Graph 에서 도달할 수 있는 놈들끼리 분류한다.
34. SCC 알고리즘을 구현하는 두가지 알고리즘
● Kosaraju algorithm
○ DFS 를 두번 돌리는 알고리즘
○ 정방향 그래프와 역방향 그래프가 같은 SCC를 가진다는 성질을 이용함
○ O(V + E)
● Tarjan algorithm
○ O(V + E)
둘 중 어떤 알고리즘을 써도 상관 없다.
35. #4 Strongly Connected Components
Void KosarajuSCC(int u) {
1. 정방향 그래프를 위상정렬 후 스택에 저장
(순회를 끝낸 노드를 스택에 push 하는 방식으로 DFS)
2. 스택이 빌 때까지 최상위 원소부터 하나씩 빼가면서
(방문하지 않았을 경우에만) 역방향 그래프에서 DFS를 수행
2-1. 위에서 수행하는 DFS에서 탐색되는 모든 노드를 SCC로 묶는다.
}