728x90
1ㄷ1 관계는 선형구조라고 불렀고 1ㄷN의 관계는 트리라고 불렀다.
그럼 N:N의 관계는 어떤 구조일까? 바로 그래프 구조라고 부른다. 비선형 구조인 그래프 구조는 그래프로 표현된 모든 자료를 빠짐없이 검색하는 것이 중요하다.
특히, 그래프와 비슷한 트리와의 큰 차이점은, 순환(Cycle) 할 수 있다는 것
이를 위한 탐색 방법 2가지가 있는데 오늘은 우선 DFS에 대해 알아볼 것이다.
- 깊이 우선 탐색 (Depth First Search, DFS)
- 너비 우선 탐색 (Breadth First Search, BFS)
두 방법의 차이
- DFS는 탐색을 한 뒤 이전의 정점으로 돌아온다
- 이것은 백트래킹(Backtracking)이라고 부른다
# DFS - 깊이 우선 탐색
- 시작 정점의 한 방향으로 갈 수 있는 경로가 있는 곳까지 깊이 탐색해 가다가 더 이상 갈 곳이 없게 되면, 가장 마지막에 만났던 갈림길 간선이 있는 정점으로 되돌아와서 다른 방향의 정점으로 탐색을 계속 반복하여 결국 모든 정점을 방문하는 순회 방법
- 가장 마지막에 만났던 갈림길의 정점으로 되돌아가서 다시 깊이 우선 탐색을 반복해야 하므로 후입 선출 구조의 스택 사용
과정
- 시작 정점 v를 결정하여 방문
- 정점 v에 인접한 정점 중에서
- 방문하지 않은 정점 w가 있으면, 정점 v를 스택에 push 하고 정점 w를 방문한다. 그리고 w를 v로 하여금 다시 2)를 반복한다.
- 반복하지 않은 정점이 없으면, 탐색의 방향을 바꾸기 위해서 스택을 pop 하여 받은 가장 마지막 방문 정점을 v로 하여 다시 2)를 반복한다.
- 스택이 공백이 될 때까지 2)를 반복한다.
visited[], stack[] 초기화
DFS(v)
시작점 v 방문;
visited[v] <- true;
while {
if ( v의 인접 정점 중 방문 안 한 정점 w가 있으면)
push(v);
v <- w; (w에 방문)
visited[w] <- true;
else
if (스택이 비어 있지 않으면)
v <- pop(stack);
else
break
}
end DFS()
그림을 보며 더 쉽게 이해해보자.
- a 노드(시작 노드)를 방문한다.
- 방문한 노드는 방문했다고 표시한다.
- a와 인접한 노드들을 차례로 순회한다.
- a와 인접한 노드가 없다면 종료한다.
- a와 이웃한 노드 b를 방문했다면, a와 인접한 또 다른 노드를 방문하기 전에 b의 이웃 노드들을 전부 방문해야 한다.
- b를 시작 정점으로 DFS를 다시 시작하여 b의 이웃 노드들을 방문한다.
- b의 분기를 전부 완벽하게 탐색했다면 다시 a에 인접한 정점들 중에서 아직 방문이 안 된 정점을 찾는다.
- 즉, b의 분기를 전부 완벽하게 탐색한 뒤에야 a의 다른 이웃 노드를 방문할 수 있다는 뜻
- 아직 방문이 안 된 정점이 없으면 종료한다.
- 있으면 다시 그 정점을 시작 정점으로 DFS를 시작한다.
구현 방법
스택 또는 재귀 방식을 이용하여 구현할 수 있다.
- 스택은 FILO(First-In, Last-Out) 방식이기 때문에 인접한 정점이 우선이 아닌 더 멀리 있는(인접 정점의 끝) 정점을 우선 탐색한 뒤 돌아올 수 있다. 큐는 FIFO라서 인접 정점을 우선하여 탐색하므로 쓸 수 없다. 큐는 BFS에 사용
- 재귀 방식으로 진행하는 것 또한 가능하다. 재귀 자체가 내부적으로 Stack을 사용하는 방식이기 때문이다. 둘 중에선 재귀 방식이 더 많이 쓰인다. 더욱 구현이 간단하기 때문이다.
시간 복잡도 : O(V+E), V는 정점 / E는 간선의 개수로 그 개수에 따라 전체 탐색에 시간이 소요된다.(인접 행렬은 O(V^2))
# 스택이용 구현
# 시작하는 정점 정보 전달 받아서
def DFS(start):
# 다음 조사 위치 쌓아 나가기 위해서
stack = [start]
# 한번 방문헀던 곳 재 방문 하지 않도록 하기위한
# 방문 가능한 크기 만큼
# 기본적으로 방문 안헀음이 default 이므로 0으로 초기화
visited = [0] * (V+1)
# 전수 조사 시작 할건데
# 언제까지 조사 할 것이냐
# stack이 빌 때까지
while stack:
# 내 조사 대상 == stack의 마지막 정점
start = stack.pop()
# 해당 정점이 아직 방문하기 전
if visited[start] == 0:
# 일단 방문
visited[start] = 1
print(start, visited)
# 모든 정점들 조사
for next in range(1, V+1):
# start 번째 기준으로 next가 이동 가능하고
# next 번째가 방문 하기 전이라면
if G[start][next] == 1 and visited[next] == 0:
# stack에 next 정점 추가
stack.append(next)
print(stack)
# 재귀이용 구현
def DFS(start):
visited[start] = 1
print(start, visited)
for next in range(1, V+1):
if G[start][next] == 1 and visited[next] == 0:
DFS(next)
응용
- 최단 경로 찾기 및 최소 스패닝 트리
- 순환 탐지
- 위상 정렬
- Puzzle 풀이 (하나의 solution) 찾기
이 외에도 굉장히 많이 사용된다. 아직 필요한 자료구조에 대해서도 많이 공부하지 못하여 간단하게만 알아보았다.
다음에 자료구조를 더 열심히 공부한 이후에 자세하게 알아보자!
728x90
'ALGORITHM > 알고리즘 알아보기' 카테고리의 다른 글
[알고리즘] 백트래킹(Backtracking) (0) | 2022.08.22 |
---|---|
[알고리즘] 후위 표기법 - 계산기 (0) | 2022.08.22 |
[알고리즘] Memoization과 동적 프로그래밍 (0) | 2022.08.17 |
알고리즘 - 검색(Search) (0) | 2022.08.10 |
알고리즘 - 부분 집합 (0) | 2022.08.10 |