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

양방향 탐색 (Bidirectional Search)

· 수정 · 📖 약 3분 · 1,001자/단어 #algorithm #search #graph #bidirectional-search
bidirectional search, 양방향 탐색, 양방향 BFS, bidirectional BFS, bidirectional-search

정의

양방향 탐색 (Bidirectional Search) 은 source 와 target 에서 동시에 탐색을 진행하여 중간에서 만나는 전략. 주로 무가중치 그래프의 최단 경로를 BFS 두 개로 찾는 양방향 BFS 형태로 쓰인다.

일반 BFS 의 O(b^d) (b = branching factor, d = depth) 를 O(b^(d/2)) 로 줄인다.

문제 상황과 동기

그래프에서 source 에서 target 까지의 최단 경로를 구해야 한다. 단방향 BFS 는 d 만큼 퍼져나가지만, 양쪽에서 동시에 확장하면 각각 d/2 까지만 확장하면 된다.

  • 단방향 BFS: O(b^d). d=10, b=10 이면 10^10 = 100억 노드.
  • 양방향 BFS: O(b^(d/2) + b^(d/2)). 같은 조건에서 10^5 + 10^5 = 20만 노드.

핵심 통찰: 탐색 공간은 깊이에 지수적으로 증가한다. 깊이를 절반으로 줄이면 탐색 공간이 제곱근으로 줄어든다.

시각화

핵심 아이디어

invariant: source 에서 출발한 frontier 와 target 에서 출발한 frontier 가 교차하는 순간이 최단 경로.

양방향 BFS:
    q_s = [source], q_t = [target]
    dist_s[source] = 0, dist_t[target] = 0

    while q_s and q_t 모두 비지 않음:
        // 더 작은 frontier 를 확장 (효율 최적화)
        if len(q_s) <= len(q_t):
            expand(q_s, dist_s, dist_t, q_t)
        else:
            expand(q_t, dist_t, dist_s, q_s)

        if 교차 발생: return dist_s[u] + dist_t[u]

알고리즘

bidirectional_bfs(graph, s, t):
    if s == t: return 0

    q_s = [s], q_t = [t]
    ds[s] = 0, dt[t] = 0

    while q_s and q_t:
        // 더 작은 쪽을 확장 (효율)
        if len(q_s) <= len(q_t):
            // q_s 의 모든 노드를 한 레벨 확장
            for u in level of q_s:
                for v in graph[u]:
                    if v in ds: continue
                    ds[v] = ds[u] + 1
                    if v in dt: return ds[v] + dt[v]
                    q_s.push(v)
        else:
            symmetric for q_t

    return -1  // 연결 안 됨

구현

// 양방향 BFS, O(b^(d/2))
#include <bits/stdc++.h>
using namespace std;

int bfs(const vector<vector<int>>& g, int s, int t) {
  if (s == t) return 0;
  vector<int> ds(g.size(), -1), dt(g.size(), -1);
  queue<int> qs, qt;
  qs.push(s); ds[s] = 0;
  qt.push(t); dt[t] = 0;

  auto expand = [&](queue<int>& q, vector<int>& d_self,
                    vector<int>& d_other) {
      int u = q.front(); q.pop();
      for (int v : g[u]) {
          if (d_self[v] != -1) continue;
          d_self[v] = d_self[u] + 1;
          if (d_other[v] != -1) return v;
          q.push(v);
      }
      return -1;
  };

  while (!qs.empty() && !qt.empty()) {
      int meet;
      if (qs.size() <= qt.size())
          meet = expand(qs, ds, dt);
      else
          meet = expand(qt, dt, ds);
      if (meet != -1) return ds[meet] + dt[meet];
  }
  return -1;
}

int main() {
  int n, m, s, t;
  cin >> n >> m >> s >> t;
  vector<vector<int>> g(n);
  while (m--) {
      int u, v; cin >> u >> v;
      g[u].push_back(v);
      g[v].push_back(u);
  }
  cout << bfs(g, s, t) << "\n";
}
stdin
6 7 0 5
0 1
0 2
1 3
2 3
3 4
4 5
2 5
결과
3

복잡도

항목
시간 (일반)O(b^(d/2))
시간 (최악)O(b^d) (연결이 끊긴 경우)
공간O(b^(d/2)) (두 frontier 모두 저장)
단방향 BFS 대비탐색 공간 제곱근 수준

d=10, b=10 기준 단방향 vs 양방향:

측정단방향 BFS양방향 BFS
방문 노드~10^10~2 x 10^5
큐 최대 크기~10^9~10^4

변형 / 활용

형태설명
양방향 BFS (무가중치)source/target 양쪽 BFS, 중간 교차
양방향 Dijkstra가중치 그래프, 양쪽 priority queue
양방향 A*heuristic 으로 방향 유도
15-puzzle / 8-puzzle상태 공간이 지나치게 커서 양방향 BFS 가 필수
단어 변환 (Word Ladder)시작 단어와 목표 단어에서 동시 확장

함정

1. disconnected graph

그래프가 연결되어 있지 않으면 양방향 BFS 도 끝까지 탐색하고 -1 리턴. 이때는 최악의 경우 단방향과 동일한 O(b^d).

2. frontier 크기 균형

항상 더 작은 큐 를 확장해야 효율이 최대. 한쪽이 너무 커지면 사실상 단방향과 다를 바 없음. if qs.size() <= qt.size() 조건 필요.

3. 거리 저장 구조

방문 여부와 거리를 O(1) 에 확인할 수 있어야 함. 배열 또는 dict 사용. Python은 dict가 느릴 수 있으니 방문 배열로 대체 가능.

4. 양방향 탐색 조건

그래프가 무방향 (undirected) 일 때 양방향 BFS 가 가장 자연스럽다. 방향 그래프에서도 가능하지만, target 에서는 incoming edge 로 확장해야 함.

5. 중복 방문 방지

양쪽 모두 visited set 을 유지. 교차 감지는 v in ds and v in dt 로. 각 확장마다 확인.

BOJ 연습 문제

번호제목정답률링크
BOJ 12851숨바꼭질 2-kokoa-lab
BOJ 13549숨바꼭질 3-kokoa-lab
BOJ 13913숨바꼭질 4-kokoa-lab
BOJ 4179불!-kokoa-lab
BOJ 2206벽 부수고 이동하기-kokoa-lab

참고

이 글의 용어 (4개)
너비 우선 탐색 (BFS)algorithm
정의 너비 우선 탐색 (Breadth-First Search, BFS) 는 그래프 G=(V, E) 에서 시작 정점 s 로부터 가까운 정점부터 순서대로 방문하는 알고리즘. 큐 (F…
다익스트라 알고리즘 (Dijkstra's Algorithm)algorithm
정의 다익스트라 알고리즘 (Dijkstra's Algorithm) 은 음이 아닌 가중치 그래프에서 단일 시작점 s 로부터 모든 정점까지의 최단 거리를 찾는 그리디 알고리즘. Ed…
A* (A-star) 알고리즘algorithm
정의 A\ 알고리즘 은 휴리스틱 기반 최단 경로 탐색 알고리즘. f(n)=g(n)+h(n) 을 평가 함수로 사용해 시작점에서 목표까지의 최단 경로를 찾는다. 1968년 Hart,…
MITM (Meet in the Middle)algorithm
정의 Meet in the Middle (MITM) 은 입력을 절반으로 나누어 각각의 모든 경우를 2^(N/2) 에 열거한 뒤, 두 결과를 합쳐서 정답을 찾는 알고리즘. naiv…

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

💬 댓글

사이트 검색 / 명령어

검색

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