본문으로 건너뛰기
김신건의 로그

이중 연결 요소 (BCC, Biconnected Component)

· 수정 · 📖 약 4분 · 1,262자/단어 #algorithm #graph #bcc #articulation #bridge #dfs
bcc, biconnected component, 이중 연결 요소, 단절점, 단절선, articulation point, bridge

정의

이중 연결 요소 (Biconnected Component, BCC) 는 무방향 그래프에서 정점 하나를 제거해도 연결성이 유지되는 최대 부분 그래프. 단절점 (articulation point) 이나 단절선 (bridge) 을 찾고, 그래프를 BCC 로 분해하는 알고리즘. 1972년 Tarjan 이 DFS low-link 기법으로 O(V+E) 선형 시간 해법 제시.

문제 상황과 동기

네트워크 신뢰성: 정점 하나 고장 시 전체 단절 가능? 어떤 간선이 bridge (제거 시 연결 요소 증가)?

  • naive: 모든 정점/간선 하나씩 지워보며 연결성 BFS/DFS. O(V·(V+E)) → 불가능.
  • Tarjan BCC: DFS 한 번에 low-link 배열 관리, O(V+E).

핵심 통찰: DFS tree 에서, 자식이 tree edge 로만 돌아올 때 그 부모가 단절점. 같은 원리로 bridge 도 O(1) 판정.

시각화

핵심 아이디어

DFS 순회 중 두 값을 추적:

  • disc[u]: DFS 도달 시각 (discovery time).
  • low[u]: u 서브트리에서 역방향 간선(back edge) 로 갈 수 있는 가장 이른 disc.

단절점 판정:

  1. 루트: 자식이 2개 이상 tree edge → 단절점.
  2. 비루트 u: 어떤 자식 v 가 low[v] >= disc[u] → u 가 단절점.

bridge 판정:

  • tree edge (u, v) 에서 low[v] > disc[u] → bridge.

BCC 분해:

  • 단절점에서 그래프가 “쪼개진다.” stack 에 간선을 쌓다가 단절점 발견 시 BCC 하나씩 pop.

block-cut tree: BCC 를 블록 노드, 단절점을 컷 노드로 만든 이분 그래프. 원 그래프의 BCC 구조를 트리로 표현.

알고리즘

BCC_Tarjan(G):
    disc[] = -1, low[] = -1, time = 0
    st = empty stack
    for u in V:
        if disc[u] < 0:
            dfs_bcc(u, -1)

dfs_bcc(u, parent):
    disc[u] = low[u] = time++
    children = 0
    for v in adj[u]:
        if v == parent: continue
        if disc[v] < 0:
            children++
            st.push((u, v))
            dfs_bcc(v, u)
            low[u] = min(low[u], low[v])

            # 단절점 조건
            if (parent < 0 and children > 1) or (parent >= 0 and low[v] >= disc[u]):
                # u 는 단절점
                pop BCC from stack until (u, v)

            # bridge 조건
            if low[v] > disc[u]:
                # (u, v) 는 bridge

        elif disc[v] < disc[u]:
            # back edge
            st.push((u, v))
            low[u] = min(low[u], disc[v])

구현

// BCC, 단절점/단절선 O(V+E)
#include <bits/stdc++.h>
using namespace std;

int n, m, timer = 0;
vector<int> adj[100005];
int disc[100005], low[100005];
bool is_cut[100005];
vector<pair<int,int>> bridges;
stack<pair<int,int>> st;
vector<vector<pair<int,int>>> bcc_list;

void dfs_bcc(int u, int p) {
  disc[u] = low[u] = timer++;
  int children = 0;
  for (int v : adj[u]) {
      if (v == p) continue;
      if (disc[v] < 0) {
          children++;
          st.push({u, v});
          dfs_bcc(v, u);
          low[u] = min(low[u], low[v]);
          // 단절점 조건
          if ((p < 0 && children > 1) || (p >= 0 && low[v] >= disc[u])) {
              is_cut[u] = true;
              vector<pair<int,int>> component;
              while (!st.empty()) {
                  auto [a, b] = st.top(); st.pop();
                  component.push_back({a, b});
                  if (a == u && b == v) break;
              }
              bcc_list.push_back(component);
          }
          // bridge 조건
          if (low[v] > disc[u]) {
              bridges.push_back({u, v});
          }
      } else if (disc[v] < disc[u]) {
          st.push({u, v});
          low[u] = min(low[u], disc[v]);
      }
  }
}

int main() {
  cin >> n >> m;
  fill(disc, disc + n, -1);
  for (int i = 0; i < m; i++) {
      int u, v; cin >> u >> v; u--; v--;
      adj[u].push_back(v);
      adj[v].push_back(u);
  }
  for (int i = 0; i < n; i++) {
      if (disc[i] < 0) dfs_bcc(i, -1);
  }
  // 마지막 남은 간선들도 BCC
  if (!st.empty()) {
      vector<pair<int,int>> component;
      while (!st.empty()) {
          component.push_back(st.top()); st.pop();
      }
      bcc_list.push_back(component);
  }
  int cut_cnt = 0;
  for (int i = 0; i < n; i++) if (is_cut[i]) cut_cnt++;
  cout << "단절점: " << cut_cnt << "\n";
  cout << "단절선: " << bridges.size() << "\n";
  cout << "BCC 개수: " << bcc_list.size() << "\n";
}
stdin
6 7
1 2
2 3
3 1
3 4
4 5
5 6
6 4
결과
단절점: 1
단절선: 1
BCC 개수: 3

복잡도

항목
시간 (최선/평균/최악)O(V + E)
공간O(V + E) (DFS stack + adjacency list)
전처리없음 (DFS 한 번)

block-cut tree

BCC 를 블록 노드로, 단절점을 컷 노드로 만든 이분 그래프.

  • 원 그래프 정점 v → 컷 노드 (단절점) 또는 블록 내부.
  • 간선: 컷 노드 ↔ 블록 노드 (v 가 속한 BCC 들).

성질:

  • 트리 (사이클 없음).
  • 경로 쿼리, LCA 등이 가능.
  • 예: 두 정점 간 경로에 몇 개의 단절점?

변형

  1. 단절점만: 루트 조건 + low[v] >= disc[u].
  2. bridge 만: low[v] > disc[u].
  3. BCC 분해: stack 으로 간선 모아 단절점에서 pop.
  4. 3-edge-connected component: bridge 를 모두 제거한 뒤 남은 연결 요소. 간선 2개 제거에도 연결.

함정

1. parent 중복 간선

무방향 그래프에서 (u, v) 를 두 번 (u→v, v→u) 저장. DFS 시 v == parent skip 필요. 멀티 엣지가 있으면 parent 한 번만 skip 해야 하므로 카운터로.

2. 루트 단절점 조건

루트는 low[v] >= disc[u] 대신 자식 tree edge 2개 이상.

3. 간선 없는 정점

disc 초기화 -1. 고립 정점은 단절점 아님.

4. BCC stack pop 타이밍

단절점 발견 시 (u,v) 까지만 pop. 전체 stack 비우면 BCC 가 섞임.

BOJ 연습 문제

번호제목정답률링크
BOJ 11266단절점-kokoa-lab
BOJ 11400단절선-kokoa-lab
BOJ 11014BCC (Block Cut Tree)-kokoa-lab
BOJ 2917늑대 사냥꾼 (bridge)-kokoa-lab

참고

이 글의 용어 (3개)
강한 연결 요소 (SCC)algorithm
정의 강한 연결 요소 (Strongly Connected Component, SCC) 는 방향 그래프에서 서로 도달 가능한 정점들의 최대 부분 집합. SCC 내 임의 두 정점 u…
깊이 우선 탐색 (DFS)algorithm
정의 깊이 우선 탐색 (Depth-First Search, DFS) 는 그래프 G=(V, E) 에서 갈 수 있는 만큼 깊이 들어가다가 막히면 백트래킹하는 알고리즘. 스택 (LIF…
최소 공통 조상 (Lowest Common Ancestor)algorithm
정의 최소 공통 조상 (LCA, Lowest Common Ancestor) 는 트리에서 두 노드 u, v 의 공통 조상 중 가장 깊은 (루트에서 가장 먼) 노드. Binary L…

이 개념을 다룬 위키 페이지 (1)

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기