본문 바로가기

강한 연결 요소(SCC) 구현 톺아보기

작성

강한 연결 요소란?

방향 있는 그래프digraph G=(V,E)G = (V, E)의 각 정점 uVu \in V에서 다른 모든 정점 vV{u}v \in V \setminus \{u\}에 도달할 수 있다면 GG는 강하게 연결되어 있다Strongly Connected고 합니다. GG의 연결된 부분 그래프, 즉 연결 요소 HH가 강하게 연결되어 있을 때, HH를 강한 연결 요소Strongly Connected Component라고 합니다.

그래프를 가장 적은 수의 강한 연결 요소로 분할하는 알고리즘을 일반적으로 강한 연결 요소 알고리즘이라 부릅니다. 정점이나 간선을 더 추가할 수 없는(극대maximal) 강한 연결 요소는 서로 겹치지 않기 때문에 이러한 분할은 유일합니다. 그래프를 강한 연결 요소로 분할하고, 강한 연결 요소에 속하는 정점들을 하나의 정점으로 묶은 그래프를 그래프의 응축condensation이라고 하며, 응축된 그래프는 사이클이 존재하지 않아 위상 정렬이 가능하고, 동적 계획법을 쉽게 적용할 수 있습니다.

이 글에서는 깊이 우선 탐색을 사용하는 세 가지 대표적인 강한 연결 요소 알고리즘을 소개합니다. 세 가지 알고리즘 모두 선형 시간 O(V+E)\mathcal{O}(V+E)에 동작합니다.

도달 가능성 판정을 사용하는 병렬 강한 연결 요소 알고리즘도 기회가 된다면 써 보고 싶습니다.

코사라주-샤리르 알고리즘

그래프 GG의 각 간선의 방향을 뒤집은 전치 그래프Transpose Graph GG^\top를 생각해 봅니다. GG의 강한 연결 요소와 GG^\top의 강한 연결 요소는 같을 것입니다.

이제 GG에서 각 정점을 모두 둘러보고 이전 정점으로 돌아가는 시점을 생각해 봅니다. 서로 다른 강한 연결 요소 AA, BB가 있을 때, 반드시 AA를 모두 둘러보고 BB를 모두 둘러보거나, 그 반대임을 알 수 있습니다. 여기서 임의로 AA를 먼저 둘러보고 BB를 둘러보게 된다고 가정하면, BB에서 AA로 가는 간선이 존재한다는 말이 됩니다. GG^\top에서는 이 간선의 방향이 반대이므로, BB에서 깊이 우선 탐색을 시작하면 AA에 도달할 수 없다는 결론을 얻게 됩니다.

그런데 같은 강한 연결 요소에 속하는 정점끼리는 GGGG^\top 모두에서 서로에게 도달 가능하기 때문에, GG에서 깊이 우선 탐색이 나중에 끝난 정점부터 GG^\top에서 깊이 우선 탐색을 하게 되면 강한 연결 요소를 하나씩 얻을 수 있습니다.

전체 그래프에 대해 깊이 우선 탐색을 정방향과 역방향으로 두 번 실행하기 때문에, 시간 복잡도는 O(V+E)\mathcal{O}(V + E)가 됩니다.

이 알고리즘에서 가장 먼저 얻은 강한 연결 요소는 GG에서 가장 처음에 둘러보기 시작하는 연결 요소라는 점도 생각해 보면 좋습니다. 코사라주-샤리르 알고리즘은 강한 연결 요소를 위상 정렬했을 때, 가장 처음에 오는 연결 요소부터 얻어냅니다.

타잔 알고리즘

다양한 그래프 알고리즘을 개발한 Robert E. Tarjan의 아이디어로, 전체 그래프에 대해 단 한 번의 깊이 우선 탐색을 통해 강한 연결 요소를 구할 수 있습니다. Tarjan이 개발한 알고리즘은 여럿 있으나 한국에서는 강한 연결 요소를 구하는 알고리즘을 가리켜 타잔 알고리즘이라 부르는 것으로 보입니다.

GG의 강한 연결 요소를 위상 정렬해 보겠습니다. 가장 마지막에 오는 강한 연결 요소는 다른 연결 요소들과 전혀 연결되어 있지 않습니다. 따라서 이 연결 요소 안에 있는 모든 정점에서 깊이 우선 탐색을 시작한 시점은 연속되어 있습니다. 이 연결 요소에 속하는 모든 정점을 제외하고 나면, 남은 연결 요소 중 마지막에 오는 것도 깊이 우선 탐색을 시작한 시점이 연속되어 있을 것입니다. 어느 정점에서 연결 요소가 시작되는지, 끝나는지를 알면 스택을 이용하여 같은 연결 요소에 속하는 정점들을 연속되게 관리할 수 있을 것으로 보입니다.

강한 연결 요소 AA에 속하는 정점 중 처음 탐색하게 되는 정점 uAu_A를 생각해 봅시다. uAu_A와 연결된 정점은 AA에 속하는 정점이거나, AA보다 나중에 오는 강한 연결 요소 BB에 속한 정점입니다. 알고리즘이 올바르다면 BB에 속한 정점들은 AA보다 먼저 처리되어 스택에서 사라질 것이므로, AA에 속한 다른 정점들과 uAu_A만을 구분하여 다른 정점들은 스택에 남기고, uAu_A의 탐색이 끝날 때 AA에 속한 모든 정점을 스택에서 꺼내올 방법이 필요합니다. AA에 속한 다른 정점들은 모두 uAu_A에 도달하는 방법이 존재하고, 다른 정점들을 탐색할 때 uAu_A는 스택에 존재한다는 사실을 이용할 수 있습니다.

각 정점 uu마다 uu에서 도달할 수 있으면서 스택에서 가장 일찍 쌓인 정점 u\ell_u를 관리합시다. uu의 탐색이 끝날 때 u=uu = \ell_u라면 u=uAu = u_A입니다. 스택에서 uu까지의 정점을 모두 삭제하고 새로운 강한 연결 요소에 배정하면 됩니다.

간선 (u,v)(u, v)를 확인할 때, vv를 아직 방문하지 않았다면 vv를 방문한 후에 u\ell_umin(u,v)\min(\ell_u, \ell_v)로 갱신합니다. v\ell_vuu에서도 도달할 수 있으므로 자연스럽습니다. vv를 이미 방문했다면 vv는 스택에 있을 수도 있고 없을 수도 있습니다. vv가 스택에 있는 경우에도 u\ell_umin(u,v)\min(\ell_u, \ell_v)로 갱신해야 합니다.* vv가 이미 방문했는데도 스택에 없다면 다른 강한 연결 요소에 속하는 정점이어서 처리가 완료된 것이므로 u\ell_u를 갱신할 필요가 없습니다.

* Tarjan의 원본 논문에서는 min(u,v)\min(\ell_u, v)로 되어 있는데, 두 방법 모두 결과에 영향을 미치지 않습니다.

전체 그래프에서 깊이 우선 탐색을 한 번 수행하고, 각 정점에 스택에서 한 번씩만 추가되고 삭제되므로 전체 시간 복잡도는 O(V+E)\mathcal{O}(V+E)입니다.

각 정점을 방문했는지, 스택에 현재 존재하는지를 관리하기 위해 11비트씩 필요하고, \ell을 저장하는 데 단어word 크기 ww비트가 필요합니다. 깊이 우선 탐색을 수행하는 과정에서 정점과 간선 번호를 저장한다고 가정하면 전체 그래프가 강하게 연결된 경우에 총 2wV2wV 비트가 필요합니다. 이때 스택 또한 크기가 VV이므로 위 알고리즘을 그대로 구현하면 총 (4w+2)V(4w+2)V 비트가 필요합니다. Pearce가 이를 개선하여 현재는 (3w+1)V(3w+1)V 비트를 요구하는 알고리즘이 알려져 있습니다. 해당 구현체는 여기를 클릭해서 확인하실 수 있습니다.

개보 알고리즘

마지막으로 설명할 알고리즘은 Gabow의 알고리즘으로, 경로 기반 강한 연결 요소 알고리즘Path-based Strong Components Algorithm의 일종입니다. 이 분류에는 여러 알고리즘이 속하고, 최초로 보고된 선형 알고리즘은 Dijkstra(1976)이지만, Gabow(2000)가 단순하고 범용적이어서 소개해 보려고 합니다.

타잔 알고리즘과 같이, 스택 SS에서 같은 연결 요소에 있는 정점을 연속되게 관리합니다.\ell을 저장하는 대신, 각 정점 uu마다 S[p]=uS[p] = u가 되는 pp를 저장해 둡니다. SSuu가 없을 수도 있는데, 이 경우는 따로 처리합니다.

동시에, 각 강한 연결 요소에서 처음 만나는 정점 uu마다 pup_u를 스택 BB로 관리해 줍니다. 처음에 pup_uuu로 정하고, uu의 탐색이 끝날 때 pu=top(B)p_u = \mathrm{top}(B)이면 정의에 따라 SS에 저장된 정점들을 모두 삭제하고 새로운 연결 요소에 배정하면 됩니다.

간선 (u,v)(u, v)를 탐색할 때, pvp_v가 존재하면서 pv<top(B)p_v \lt \mathrm{top}(B)라는 것은 uu에서 vv로 도달할 수 있으면서 스택에 vv부터 uu까지 이르는 경로가 존재한다는 뜻입니다. 이 정점들은 모두 같은 연결 요소에 속해야 하므로 조건을 만족하지 않을 때까지 BB의 맨 마지막 원소를 제거해 줍니다. pvp_v가 존재하지 않는 경우는 아직 vv를 한 번도 방문하지 않았거나 vv가 이미 강한 연결 요소에 배정된 경우뿐입니다. 후자의 경우 추가적인 처리가 필요하지 않으므로 무시해도 됩니다.

전체 그래프에서 깊이 우선 탐색을 한 번 하고, 스택 SSBB에서 각 정점이 한 번씩 추가되고 삭제되므로 시간복잡도는 O(V+E)\mathcal{O}(V + E)입니다.


이상으로 세 가지 강한 연결 요소 알고리즘을 소개해 보았습니다. 세 알고리즘 모두 문제의 접근 방식과 해결 방법이 아름다운 것 같습니다. 이 글이 그래프에 대한 이해를 조금 더 깊게 할 수 있었으면 좋겠습니다.

틀린 내용이나 추가할 내용 등이 있는 경우 댓글로 알려주시면 감사하겠습니다.