단절점(Articulation Point) 기준으로 나누는 방법edge를 기준으로 생각 (목차 3&4 참조) 1) DFN: DFS를 통해 각 vertex의 방문 숫자(DFN, DFS Number)를 기록한다
2) Low: Back Edge를 이용하여 도달할 수 있는 가장 빠른 vertex의 DFN을 찾는다
3) Articulation Point(child) 판별법: low[v]>=dfn[u] (u: parent / v: child) 를 만족하는 u가 Articulation Point(단절점)이다.
4) Articulation Point(root) 판별법: (DFS tree에서) root의 자식이 2개 이상이면 root는 Articulation Point이다.
5) Stack: dfs를 통해 어떤 BCC인지 확정되지 않은 edge를 저장하는 공간
6) BCC 구성하기: Articulation Point가 확정되면, edge를 stack에서 꺼내와 BCC 구성하기
각 vertex가 몇 번째로 방문되었는지 기록하는 array정의 dfn[v] = (dfs에서) vertex인 'v'가 몇 번째로 방문했는지 기록<java>
List<Integer>[] adjList; //graph
boolean[] visited; //방문 여부 확인
int sequence = 1; //방문한 순서 count
int[] dfn; //dfn (dfs number)
...
// BCC - DFN 함수 구현
// v: 현재 vertex
// u: parent
void bc(int v, int u){
visited[v] = true; //방문 여부 기록
dfn[v] = sequence++; //방문 순서 기록
// v의 인접 vertex 'e'에 대한 dfs 실행
// 주의! v의 child가 아닌 단순한 '인접 vertex'임
for(int e: adjList[v]){
if(!visited[e]) {
bc(e, v);
...
}
}
...
}
예시)

Articulation Point(단절점) 특정 vertex를 제거했을 때, 두 개 이상의 component로 나눌 수 있는 vertexBiconnected Component 임의의 두 정점 사이에 두 개 이상의 vertex-disjoint path가 존재하는 최대 부분그래프단절점을 기준으로 나뉜다.예시)




<java>
List<Integer>[] adjList; //graph
boolean[] visited; //방문 여부 확인
int sequence = 1; //방문한 순서 count
int[] dfn; //dfn (dfs number)
...
// BCC - DFN 함수 구현
// v: 현재 vertex
// u: parent
void bc(int v, int u){
visited[v] = true; //방문 여부 기록
dfn[v] = sequence++; //방문 순서 기록
// v의 child인 'e'에 대한 dfs 실행
for(int e: adjList[v]) {
if(!visited[e]) {
bc(e, v);
// 현재 'v'에 대한 back edge 찾기
} else if(e!=u && dfn[e]<dfn[v]) {
}
}
...
}
분절점을 찾기 위한 도구
정의 low[v] = subtree of 'v'가 Back Edge를 통해 얼마나 위쪽 ancestor(조상)까지 올라갈 수 있는지를 나타내는 값
low[v]가 갱신될 수 있는 두 가지 케이스
Back Edge의 발견
ancestor(조상)으로 가는 edge가 존재low[v]가 dfn[e]로 더 작아질 가능성 존재low[v] = Math.min(low[v], dfn[e])ex) 1->2->3->4->5->6->7 (->2)
1) vertex '7'에서 vertex '2'로 갈 수 있는 edge 존재
2) low[7] = Math.min(low[7], dfn[2]) = Math(7, 2) = 2로 갱신
해당 'v'의 subtree에 대한 모든 DFS가 끝남
해당 'v'의 subtree에 해당하는 vertex의 low가 끝남
재귀적으로 return하며 low[v] 자동적으로 초기화
low[v] = Math.min(low[v], low[e])
ex) 1->2->3->4->5->6->7 (->2)
1) low[7]=2로 갱신 후, vertex '7'에 대한 child가 없으니 return
2) vertex '6'에 대한 low[6] = Math.min(low[6], low[7]) = Math.min(6, 2) = 2로 갱신
3) vertex '5'에 대한 low[5] = Math.min(low[5], low[7]) = Math.min(5, 2) = 2로 갱신
4) ... 위 알고리즘을 통해 low[3]까지 2로 갱신
<java>
List<Integer>[] adjList; //graph
boolean[] visited; //방문 여부 확인
int sequence = 1; //방문한 순서 count
int[] dfn; //dfn (dfs number)
int[] low; //low
// BCC - DFN 함수 구현
// v: 현재 vertex
// u: parent
void bc(int v, int u){
visited[v] = true; //방문 여부 기록
dfn[v] = sequence++; //방문 순서 기록
// v의 child인 'e'에 대한 dfs 실행
for(int e: adjList[v]) {
if(!visited[e]) {
bc(e, v);
//*2)번 조건으로 해당 'v'의 subtree에 대한 low[v] 갱신
low[v] = Math.min(low[v], low[e]);
// 현재 'v'에 대한 back edge 찾기
} else if(e!=u && dfn[e]<dfn[v]) {
//*1)번 조건으로 Back Edge 발견 후 low[v] 갱신
low[v] = Math.min(low[v], dfs[e]);
}
}
...
}
일반적인 vertex low[e]>=dfn[v]DFS root root의 child vertex >= 2root가 2개 이상의 child를 가지면 항상 root는 Articulation Point<java>
List<Integer>[] adjList; //graph
boolean[] visited; //방문 여부 확인
int sequence = 1; //방문한 순서 count
int[] dfn; //dfn (dfs number)
int[] low; //low
// BCC - DFN 함수 구현
// v: 현재 vertex
// u: parent
void bc(int v, int u){
visited[v] = true; //방문 여부 기록
dfn[v] = sequence++; //방문 순서 기록
// v의 child인 'e'에 대한 dfs 실행
for(int e: adjList[v]) {
if(!visited[e]) {
bc(e, v);
//*2)번 조건으로 해당 'v'의 subtree에 대한 low[v] 갱신
low[v] = Math.min(low[v], low[e]);
//*모든 low가 갱신됨 -> 단절점 찾기
if (low[e] >= dfn[v]) {}
//현재 'v'에 대한 back edge 찾기
} else if(e!=u && dfn[e]<dfn[v]) {
//*1)번 조건으로 Back Edge 발견 후 low[v] 갱신
low[v] = Math.min(low[v], dfn[e]);
}
}
...
}
<java>
List<Integer>[] adjList; //graph
boolean[] visited; //방문 여부 확인
int sequence = 1; //방문한 순서 count
int[] dfn; //dfn (dfs number)
int[] low; //low
Stack<Edge> stack; //edge를 임시저장하는 stack
// BCC - DFN 함수 구현
// v: 현재 vertex
// u: parent
void bc(int v, int u){
visited[v] = true; //방문 여부 기록
dfn[v] = sequence++; //방문 순서 기록
low[v] = dfn[v]; //low 초기화 (초기에는 dfn과 같음)
// v의 child인 'e'에 대한 dfs 실행
for(int e: adjList[v]) {
if(!visited[e]) {
// 방문하지 않은 edge 발견 -> stack에 추가 (Tree Edge)
stack.push(new Edge(v, e));
bc(e, v);
//*2)번 조건으로 해당 'v'의 subtree에 대한 low[v] 갱신
low[v] = Math.min(low[v], low[e]);
// 모든 low가 갱신됨 -> 단절점 찾기
if (low[e] >= dfn[v]) {
//TODO: BCC 구성하기
}
// 현재 'v'에 대한 back edge 찾기
} else if(e!=u && dfn[e]<dfn[v]) {
// back edge 발견 -> stack에 추가 (Back Edge)
stack.push(new Edge(v, e));
//*1)번 조건으로 Back Edge 발견 후 low[v] 갱신
low[v] = Math.min(low[v], dfn[e]);
}
}
}
BCC 구성하기 Articulation Point를 기점으로 하나의 BCC 구성하기<java>
List<Integer>[] adjList; //graph
boolean[] visited; //방문 여부 확인
int sequence = 1; //방문한 순서 count
int[] dfn; //dfn (dfs number)
int[] low; //low
Stack<Edge> stack; //edge를 임시저장하는 stack
// BCC - DFN 함수 구현
// v: 현재 vertex
// u: parent
void bc(int v, int u){
visited[v] = true; //방문 여부 기록
dfn[v] = sequence++; //방문 순서 기록
low[v] = dfn[v]; //low 초기화 (초기에는 dfn과 같음)
// v의 child인 'e'에 대한 dfs 실행
for(int e: adjList[v]) {
if(!visited[e]) {
// 방문하지 않은 edge 발견 -> stack에 추가 (Tree Edge)
stack.push(new Edge(v, e));
bc(e, v);
//*2)번 조건으로 해당 'v'의 subtree에 대한 low[v] 갱신
low[v] = Math.min(low[v], low[e]);
// 모든 low가 갱신됨 -> 단절점 찾기
if (low[e] >= dfn[v]) {
//BCC 구성하기
while(true){
Edge edge = stack.pop();
//마지막 Tree Edge (v, e)까지 꺼내면 종료
if(edge.adjvertex1 == v && edge.adjvertex2 == e){ break; }
}
}
// 현재 'v'에 대한 back edge 찾기
} else if(e!=u && dfn[e]<dfn[v]) {
// back edge 발견 -> stack에 추가 (Back Edge)
stack.push(new Edge(v, e));
//*1)번 조건으로 Back Edge 발견 후 low[v] 갱신
low[v] = Math.min(low[v], dfn[e]);
}
}
}