[알고리즘] Graph algorithm - Depth First Search, Topological ordering

 

그래프 알고리즘 I: 깊이 우선 탐색 (DFS)

그래프는 정점(vertex)과 간선(edge)으로 이루어진 자료 구조다. 정점(vertex)은 데이터를 나타내고, 간선(edge)은 정점 간의 관계를 나타낸다. 그래프는 다양한 문제를 모델링하는 데 사용될 수 있으며, 소셜 네트워크, 지도, 웹 페이지 연결 등을 표현하는 데 유용하다. 자세한 내용은 자료구조 시간에 다뤘으므로 참고하도록 하자.

그래프 표현 방식

그래프는 크게 두 가지 방식으로 표현할 수 있다.

  1. 인접 행렬 (Adjacency Matrix): 2차원 배열을 사용하여 정점 간의 연결 관계를 표현한다. 행렬의 각 요소는 두 정점 사이에 간선이 존재하는지 여부를 나타낸다.
    adjacency matrix

  2. 연결 리스트 (Linked List): 각 정점에 대해 연결된 정점들의 리스트를 저장한다.
    linked list

인접 행렬과 연결 리스트의 시간 복잡도 분석

두 표현 방식은 각각 장단점을 가지고 있다. 인접 행렬은 간선 존재 여부를 확인하는 데 \(O(1) \) 시간이 걸리지만, 모든 간선을 확인하는 데 \(O(n^{2}) \) 시간이 걸린다. 반면 연결 리스트는 간선 존재 여부를 확인하는 데 \(O(deg(v)) \) 시간(이때 deg는 degree 즉 v에서 나가는 간선의 수)이 걸리지만, 모든 간선을 확인하는 데 \(O(n+m) \) 시간이 걸린다. 여기서 n은 정점의 수, m은 간선의 수, deg(v)는 정점 v의 차수(degree)를 의미한다.

left : adjacency matrix, right : linked list

따라서 간선이 많은 밀집 그래프(Dense Graph)의 경우 인접 행렬이 유리하며, 간선이 적은 희소 그래프(Sparse Graph)의 경우 연결 리스트가 유리하다.


깊이 우선 탐색 (DFS)

깊이 우선 탐색 (Depth First Search, DFS)은 그래프를 탐색하는 알고리즘 중 하나다. DFS는 시작 정점에서부터 가능한 깊이까지 탐색을 진행하고, 더 이상 진행할 수 없으면 이전 정점으로 돌아와 다른 경로를 탐색하는 방식으로 동작한다.


DFS의 작동 방식

DFS는 각 정점을 다음 세 가지 상태로 분류한다.

  1. Unvisited: 아직 탐색하지 않은 정점
  2. In Progress: 현재 탐색 중인 정점
  3. All Done: 탐색이 완료된 정점

DFS는 시작 정점을 '진행 중'으로 표시하고, 인접한 정점 중 '방문하지 않은' 정점을 재귀적으로 방문한다. 더 이상 방문할 정점이 없으면 현재 정점을 '모두 완료'로 표시하고 이전 정점으로 돌아간다.

Depth First Search algorithm


DFS의 의사 코드 (Pseudocode)

DFS(w, currentTime):
  w.startTime = currentTime
  currentTime++
  Mark w as in progress
  for v in w.neighbors:
    if v is unvisited:
      currentTime = DFS(v, currentTime)
      currentTime++
  w.finishTime = currentTime
  Mark w as all done
  return currentTime

이 때 DFS는 각 간선을 오직 한 번 방문하기 때문에, 실행 시간은 \(O(m) \)이다. 여기서 m은 간선의 수이다. (linked list로 구현한 DFS를 가정)


그리고 모든 정점에 대하여 DFS를 호출하면, N개의 정점을 탐색해야 하므로, 실행 시간은 \(O(n + m) \)이 된다. 참고로 adjacency matrix로 구현하면 실행 시간은 \(O(n^{2}) \)이다.

DFS to explore the whole thing

이를 이용해 DFS를 연결 요소(Connected Component)를 찾거나 in-order traversal, exact traversal을 할 수 있다.

DFS의 특징: 트리와 유사한 구조

DFS는 탐색 과정에서 각 정점의 방문 시간과 완료 시간을 기록한다. 이러한 정보를 이용하면 DFS 트리(DFS Tree)를 구성할 수 있다. DFS 트리는 DFS 탐색 과정을 트리 형태로 나타낸 것으로, 루트는 시작 정점이고, 각 정점의 자식은 DFS 탐색 순서대로 방문한 정점들이다. DFS 트리는 그래프의 구조를 파악하고, 사이클(cycle)을 찾는 데 유용하게 사용된다.

DFS tree


위상 정렬 (Topological Ordering)

DFS의 활용 사례로는 위상 정렬이 있다. 위상 정렬은 방향 그래프(Directed Graph)의 정점들을 순서대로 나열하는 것으로, 모든 간선 (u, v)에 대해 정점 u가 정점 v보다 앞에 오도록 하는 것이다. 쉽게 말해, 우선순위에 맞춰 정렬하는 것을 말한다. 위상 정렬은 선수 과목 찾기, 작업 스케줄링, 컴파일러 의존성 분석 등에 사용된다.

Prerequisite subjects

위와 같은 Graph를 방향 비순환 그래프 (Directed Acyclic Graph, DAG)라고 하는데, 이는 사이클이 없는, 즉 되돌아오지 않는 방향 그래프이다. 위상 정렬은 DAG에서만 가능하다.


위상 정렬의 원리

앞서 우리는 DFS의 pesudocode에서 time을 기록한 적이 있다. 위상 정렬은 Finish time의 아이디어를 이용해 구현된다. DFS 탐색을 수행하면서 각 정점의 완료 시간을 기록하고, 완료 시간이 늦은 정점부터 출력하면 위상 정렬 순서가 된다.

time recording in DFS

위의 예시에서 finish time이 클수록 먼저 가장 우선시된다는 점을 파악할 수 있다.
이를 보다 엄밀히 증명해보면 다음과 같다.

parentheses theorem
Parentheses theorem을 이용해 일반화하면 위와 같이 증명할 수 있다. 이는 사이클이 있는 상황에서도 성립한다.

  • v는 w의 자손인 경우 : DFS의 작동 방식에 의해 DFS tree에서 가장 위에 있는 node가 가장 먼저 실행되고, 가장 마지막으로 끝나므로 finish time은 v < w이다.
  • v가 w의 자손이 아닌 경우 : DFS의 작동 방식에 의해 v가 먼저 실행 및 종료되고, w가 나중에 실행 및 종료되므로 finish time은 v < w이다.
이러한 성질 덕분에 DFS를 이용하여 위상 정렬이 가능하다.

마치며

이번 포스팅에서는 그래프 알고리즘의 기본 개념과 깊이 우선 탐색 (DFS)에 대해 알아보았다. DFS는 그래프 탐색, 연결 요소 찾기, 위상 정렬 등 다양한 문제를 해결하는 데 사용되는 중요한 알고리즘이다. 다음 시간에는 또 다른 탐색 알고리즘인 너비 우선 탐색 (BFS)에 대해 알아보고자 한다.

추천글:

[알고리즘] hash table의 worst case를 극복하는, Self-Balancing Binary Search Tree(Red-Black Tree)
(https://hyeondev.blogspot.com/2024/10/hash-table-worst-case-self-balancing.html)

[자료구조] DFS(깊이우선탐색) & BFS(넓이우선탐색)
hyeon_B

안녕하세요! AI 기술을 이용해 더 나은 세상을 만들어 나가고 싶은 과기원생 Hyeon이라고 합니다. 저는 앞으로 인공지능 시대에는 지식을 '활용'하는 능력이 중요해질 것이라고 생각합니다. 대부분의 일들은 인공지능이 뛰어난 모습을 보이지만, 인공지능은 데이터로 부터 연관관계를 학습하기 때문에 지식들을 새로 통합해서 활용하는 능력이 부족합니다. 인공지능이 뉴턴 전에 만들어졌다면 사과가 떨어지는 이유에 대답하지 못했을 것이고, 아인슈타인 전에 만들어졌다면 중력이 어떻게 생기는지 설명하지 못했을 것입니다. 따라서 앞으로 우리는 '본질'을 탐구하고 그 본질로부터 다른 곳에 적용하며 인공지능을 현명하게 활용해야 할 것입니다. 함께 인공지능 시대를 준비합시다!

댓글 쓰기

다음 이전

POST ADS1

POST ADS 2