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

Dial's Algorithm (버킷 기반 다익스트라)

· 수정 · 📖 약 2분 · 761자/단어 #algorithm #search #shortest-path #dial
dial, Dial, Dial's algorithm, 버킷 다익스트라, bucket dijkstra, dial algorithm

정의

Dial’s Algorithm 은 가중치가 작은 정수 일 때 우선순위 큐 대신 버킷 배열 을 사용해 Dijkstra 를 O(V + E + W) 로 최적화한 알고리즘. W는 최대 가중치.

1979년 Robert Dial이 제안. 간선 가중치가 0~C 의 작은 정수일 때만 적용 가능.

문제 상황과 동기

Dijkstra에서 priority_queue 의 extract-min 은 O(log V). 작은 정수 가중치(W ≤ 1000 정도) 그래프가 매우 많다.

  • Dijkstra (heap): O((V+E) log V)
  • Dial: 버킷 배열로 extract-min 을 O(1) amortized 로 떨어뜨림. 총 O(V + E + W)

핵심 통찰: 거리 차가 최대 W 이므로, 현재 거리에서 W 이내 범위만 버킷에 존재. 원형 버킷 배열로 공간 O(W).

시각화

핵심 아이디어

거리 dist[v] 를 버킷 인덱스로 사용. bucket[t mod (W+1)] 에 정점을 저장.

W = max edge weight
버킷 크기 = W + 1
i = 0..W-1 (현재 확장 위치)

dist[s] = 0, bucket[0] = {s}

while 처리 안 한 버킷 있음:
    while bucket[i] not empty:
        v = bucket[i]에서 하나 꺼냄
        for each u in adj[v]:
            w = weight(v, u)
            if dist[v] + w < dist[u]:
                old = dist[u], dist[u] = dist[v] + w
                bucket[old % (W+1)] 에서 제거
                bucket[dist[u] % (W+1)] 에 추가
    i = (i + 1) % (W + 1)

dist[v] 의 범위: 현 시점 최소 dist 부터 최대 dist+W 까지만 유효. 따라서 원형 버킷 배열이면 충분.

구현

// Dial: O(V + E + W), 가중치 0..C 정수
#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9;

int dial(vector<vector<pair<int,int>>>& adj, int s, int t, int W) {
  int n = adj.size();
  vector<int> dist(n, INF);
  dist[s] = 0;

  int bucket_size = W + 1;
  vector<list<int>> buckets(bucket_size);
  vector<list<int>::iterator> pos(n, buckets[0].end());

  buckets[0].push_back(s);
  pos[s] = buckets[0].begin();

  int cur = 0, processed = 0;
  while (processed < n) {
      while (buckets[cur % bucket_size].empty()) {
          cur++;
          if (cur - dist[s] > W * n) return -1; // 도달 불가
      }
      int v = buckets[cur % bucket_size].front();
      buckets[cur % bucket_size].pop_front();
      processed++;

      if (dist[v] != cur) continue; // stale entry

      for (auto [u, w] : adj[v]) {
          int nd = dist[v] + w;
          if (nd < dist[u]) {
              if (dist[u] != INF)
                  buckets[dist[u] % bucket_size].erase(pos[u]);
              dist[u] = nd;
              int bid = nd % bucket_size;
              buckets[bid].push_back(u);
              pos[u] = prev(buckets[bid].end());
          }
      }
  }
  return dist[t];
}

int main() {
  // V=4, s=0 -> t=3, W=5
  vector<vector<pair<int,int>>> adj(4);
  adj[0].push_back({1,2});
  adj[0].push_back({2,3});
  adj[1].push_back({3,1});
  adj[2].push_back({3,4});

  cout << dial(adj, 0, 3, 5);
  return 0;
}
stdin
4 nodes, s=0 t=3, maxW=5
결과
3

복잡도

항목
시간 (최선)O(V + E) - 가중치 0 일 때
시간 (평균)O(V + E + W)
시간 (최악)O(V + E + W)
공간O(V + W)
제약가중치 0 이상 정수, W 가 작아야 효율적

변형 / 활용

  • Denardo-Fox (1979): 버킷 크기를 동적 조정.
  • R-Heaps (Ahuja): 버킷 범위를 dial 보다 더 넓게.
  • 작은 W 그래프: 교통망 (도로 속도 제한), 네트워크 패킷(hop count bounded).
  • E=V+1000, W=1000 이면 O(E + W) 가 O(E log V) 보다 빠름.

함정

1. W 가 크면 오히려 느림

W = 10^9 면 버킷 크기 10^9 필요. 그땐 heap Dijkstra.

2. 음수 가중치 불가

Dial은 non-negative 전제. 음수는 Bellman-Ford.

3. 버킷 관리 오버헤드

remove(u) 에서 O(1) 이 보장되어야 함. C++ list+iterator 또는 Python list+index 관리 필요.

BOJ 연습 문제

번호제목정답률링크
BOJ 1753최단경로 (Dijkstra 연습)-kokoa-lab
BOJ 1916최소비용 구하기-kokoa-lab
BOJ 4485녹색 옷 입은 애가 젤다지?-kokoa-lab

참고

이 글의 용어 (3개)
너비 우선 탐색 (BFS)algorithm
정의 너비 우선 탐색 (Breadth-First Search, BFS) 는 그래프 G=(V, E) 에서 시작 정점 s 로부터 가까운 정점부터 순서대로 방문하는 알고리즘. 큐 (F…
다익스트라 알고리즘 (Dijkstra's Algorithm)algorithm
정의 다익스트라 알고리즘 (Dijkstra's Algorithm) 은 음이 아닌 가중치 그래프에서 단일 시작점 s 로부터 모든 정점까지의 최단 거리를 찾는 그리디 알고리즘. Ed…
벨만-포드 알고리즘 (Bellman-Ford Algorithm)algorithm
정의 벨만-포드 알고리즘 (Bellman-Ford Algorithm) 은 음수 가중치 간선을 허용하면서 단일 시작점 s 로부터 모든 정점까지의 최단 거리를 찾는 DP 기반 알고리…

💬 댓글

사이트 검색 / 명령어

검색

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