깊이우선탐색 - DFS
요약
그래프의 완전 탐색 기법 중 하나이며 시작노드에서 출발 -> 탐색할 분기를 정해서 최대 깊이까지 탐색을 마친 후, 다른 쪽 분기로 넘어가 다시 탐색하는 알고리즘으로 재귀함수로 구현한다.
분석
DFS는 한 번 방문한 노드를 재방문해선 안 되기 때문에 방문 여부를 따로 체크할 리스트가 필요하다.
DFS 탐색방식은 후입 선출 특성을 가지므로 스택을 사용한다.
1. 방향 없는 그래프
방향없는 그래프에서 연결요소를 구해보자
예시
아래는 입출력의 예시다.
입력 예시
=================
6 5
1 2
2 5
5 1
3 4
4 6
이 숫자의 나열은 첫 번째 줄은 노드의 숫자와 엣지의 개수를 말하며
그 아래로는 어느 노드와 어느 노드가 연결되어 있는지를 나타낸다.
즉, 노드는 총 6개, 노드간의 연결은 총 5개이며
1번과 2번이 연결, 2번과 5번이 연결, 5번과 1번이 연결......이라고 할 수 있으며
이를 그림으로 표현하면 아래와 같다.
이 그래프는 보시다시피 두 개가 서로 분리되어 있다.
고로 출력 결과는 아래와 같다.
출력 예시
============
2
이번엔 다른 입력 예시를 만들어보자
입력 예시
==============
6 8
1 2
2 5
5 1
3 4
4 6
5 4
2 4
2 3
노드는 6개 연결은 총 8개며 그림으로 표현하면 다음과 같다.
보시다시피 따로 분리되어 있지 않다 고로 출력결과는 1이 나온다.
출력 예시
================
1
코드
import sys
sys.setrecursionlimit(10000)
input = sys.stdin.readline
n, m = map(int, input().split())
A = [[] for _ in range(n + 1)]
visited = [False] * (n + 1)
def DFS(v):
visited[v] = True
for i in A[v]:
if not visited[i]:
DFS(i)
for _ in range(m):
s, e = map(int, input().split())
A[s].append(e)
A[e].append(s)
count = 0
for i in range(1, n + 1):
if not visited[i]:
count += 1
DFS(i)
print(count)
우선 노드와 엣지수를 입력을 받으면
A리스트를 생성하는데, 이 리스트는 각 노드가 어디와 연결되어 있는지를 나타내는 리스트다
예를 들어, 이 사진어서, 1번 노드는 2번과 5번과 연결되어 있으므로 A[1] 의 값은 [2, 5]가 된다.
visited는 각 노드별로 방문을 했는지 안했는지를 나타내는 리스트로 일단은 전부 False로 입력된다.
첫 번째 for문에서, 각 이을 노드 번호를 s와 e로 입력 받고
이것을 A리스트에 추가한다.
이것을 엣지 수만큼 반복하면 각 노드가 어느 노드와 연결되어 있는지 나타내는 리스트 A가 완성된다.
위 예시 기준, A값은 다음과 같이 나온다.
A[[], [2, 5], [1, 5, 4, 3], [4, 2], [3, 6, 5, 2], [2, 1, 4], [4]]
두 번째 for문에서 DFS 함수를 실행한다.
DFS 함수를 실행했다는건 해당노드를 방문했다는 뜻이므로 해당 visited의 값은 True가 된다.
이 for문은 1부터 시작한다. 즉 DFS 함수의 파라미터에 1을 넣고
안의 for문에서 visited의 값을 체크한다.
이걸 계속 돌면서, visited 값을 True로 바꿔나가다보면 연결된 모든 노드들이 방문하면서, visited 변수의 값이 True로 바뀐다.
이걸 몇 번했느냐가 곧 count 변수의 값이다.
예시에선 2 번째 for문에서 DFS를 한 번 호출 했으므로 count값은 1이 된다.
그리고 이 count 값이 연결 요소의 개수이다.
'Algorithm' 카테고리의 다른 글
[알고리즘] 재귀 호출 (0) | 2023.02.26 |
---|---|
[알고리즘] 1차원 배열과 2차원 배열의 구간 합 (Prefix Sum) (0) | 2023.02.26 |