다익스트라 알고리즘 (Dijkstra's Algorithm)

한눈에 보기

음수 간선이 없는 그래프에서 한 정점으로부터의 최단 거리를 구하는 다익스트라 알고리즘의 그리디 원리, 우선순위 큐 구현, 복잡도 비교와 한계를 정리한다.

최종 업데이트 2026.09.17

다익스트라(Dijkstra) 알고리즘은 간선마다 비용이 다른 그래프에서 한 정점으로부터 나머지 모든 정점까지의 최단 거리를 구한다. BFS는 모든 간선의 비용이 같을 때만 최단 거리를 주므로, 비용이 제각각이면 다익스트라가 필요하다.

핵심 아이디어는 "아직 확정되지 않은 정점 중 가장 가까운 것부터 확정한다"는 탐욕 전략이다. 이 "가장 가까운 것 고르기"를 우선순위 큐로 처리하면 O(E log V)에 끝난다. 코딩테스트에서는 이 힙 버전만 알면 충분하다.

개념과 원리

시작점의 거리를 0, 나머지를 무한대로 두고 시작한다. 그리고 이 두 가지를 반복한다.

  1. 아직 확정되지 않은 정점 중 현재 기록된 거리가 가장 짧은 정점을 고른다. 그 거리를 최종 확정한다.
  2. 그 정점에서 나가는 간선들을 따라가 보며, 더 짧은 경로가 되면 기록을 갱신한다. 이것을 완화(relaxation)라고 부른다.

1번에서 고른 정점의 거리를 왜 바로 확정해도 되는가? 그 정점보다 가까운 미확정 정점이 없으니, 다른 경로를 돌아서 가면 반드시 더 멀어지기 때문이다. 간선 비용이 음수가 아니라는 전제가 여기서 쓰인다.

다익스트라 알고리즘: 다섯 개 정점으로 이루어진 가중치 그래프와 시작점 1로부터 각 정점까지의 최단 거리
정점 2로 가는 직행 간선은 비용 4지만, 3을 거치면 1 + 2 = 3으로 더 싸다. 이렇게 더 나은 경로를 발견해 기록을 줄이는 것이 완화다.

진행 과정 따라가기

위 그래프에서 정점 1을 시작점으로 돌려보면 이렇게 흘러간다. 표의 값은 각 시점에 기록된 최선의 거리이고, ∞는 아직 도달하지 못했다는 뜻이다.

확정한 정점12345이때 일어난 일
시작0∞∞∞∞시작점만 0
1 (거리 0)041∞∞1→2와 1→3을 완화
3 (거리 1)031∞93을 거치니 2가 4→3으로 줄었다
2 (거리 3)031892→4를 완화
4 (거리 8)031898+1=9는 기존 9와 같아 갱신 없음
5 (거리 9)03189모든 정점 확정, 종료

매번 미확정 정점 중 가장 작은 값을 골랐다는 점에 주목하자. 1 → 3 → 2 → 4 → 5 순으로 확정됐고, 이 순서는 정점 번호와도 간선 개수와도 무관하게 오직 거리 순이다.

음수 간선이 있으면 왜 안 되는가

다익스트라는 "한 번 확정한 정점은 다시 보지 않는다"는 전제 위에서 동작한다. 음수 간선이 있으면 이 전제가 깨진다. 나중에 훨씬 싼 경로가 나타날 수 있기 때문이다.

다익스트라 알고리즘: 음수 간선이 있는 그래프에서 다익스트라가 잘못된 최단 거리를 내놓는 반례
정점 2를 거리 2로 확정한 뒤에야 3을 거치는 더 싼 경로가 드러나지만, 이미 확정한 값은 되돌리지 않는다. 음수 간선이 있으면 벨만-포드를 써야 한다.

시간 복잡도

구현 방식복잡도설명
배열로 최솟값 찾기O(V²)매번 전체를 훑어 최소를 찾는다
우선순위 큐 (힙)O(E log V)코딩테스트의 표준

간선이 정점 수에 비해 적은 희소 그래프에서는 힙 버전이 압도적으로 빠르다. 반대로 간선이 거의 모든 쌍에 존재하는 밀집 그래프(E ≈ V²)라면 O(V²) 버전이 오히려 나을 수도 있지만, 코딩테스트에서 그럴 일은 드물다.

구현 코드 예시

import heapq

INF = float('inf')

def dijkstra(graph, start, n):
    """graph[v] = [(다음 정점, 비용), ...] 형태의 인접 리스트."""
    dist = [INF] * (n + 1)
    dist[start] = 0
    pq = [(0, start)]                 # (거리, 정점) — 거리가 앞에 와야 거리 기준으로 정렬된다

    while pq:
        d, v = heapq.heappop(pq)

        if d > dist[v]:               # 이미 더 짧은 경로로 처리한 정점이면 버린다
            continue

        for nxt, w in graph[v]:
            nd = d + w
            if nd < dist[nxt]:        # 더 짧아질 때만 갱신하고 큐에 넣는다
                dist[nxt] = nd
                heapq.heappush(pq, (nd, nxt))

    return dist

if d > dist[v]: continue 두 줄이 이 구현의 핵심이다. 힙에서 원소를 빼는 기능이 없어서 갱신할 때마다 새 항목을 밀어 넣는데, 그러다 보면 같은 정점이 여러 번 들어간다. 꺼낼 때 "이 값이 지금 기록보다 크면 낡은 정보"라고 판단해 건너뛰는 것이다. 이 검사를 빼도 답은 맞지만 불필요한 순회가 늘어 느려진다.

경로 자체가 필요하면 이전 정점을 기록해두고 거꾸로 따라가면 된다.

def dijkstra_with_path(graph, start, end, n):
    dist = [INF] * (n + 1)
    prev = [0] * (n + 1)              # 어디서 왔는지 기록
    dist[start] = 0
    pq = [(0, start)]

    while pq:
        d, v = heapq.heappop(pq)
        if d > dist[v]:
            continue
        for nxt, w in graph[v]:
            if d + w < dist[nxt]:
                dist[nxt] = d + w
                prev[nxt] = v
                heapq.heappush(pq, (d + w, nxt))

    path, cur = [], end               # 끝에서부터 거슬러 올라간다
    while cur != start:
        path.append(cur)
        cur = prev[cur]
    path.append(start)
    return dist[end], path[::-1]

어떤 알고리즘을 쓸 것인가

상황알고리즘복잡도
모든 간선 비용이 같음BFSO(V + E)
비용이 다르고 전부 0 이상다익스트라O(E log V)
음수 간선이 있음벨만-포드O(VE)
모든 쌍의 최단 거리플로이드-워셜O(V³)
비용이 0 또는 1뿐0-1 BFS (덱)O(V + E)

자주 하는 실수

  • 힙에 (정점, 거리) 순으로 넣는다. 파이썬 힙은 튜플의 첫 원소로 비교하므로 거리가 앞이어야 한다. 순서를 바꾸면 정점 번호가 작은 것부터 꺼내져서 완전히 다른 알고리즘이 된다.
  • 방문 배열을 따로 만들어 관리한다. visited를 두는 대신 d > dist[v]로 걸러내는 편이 간결하고 실수가 적다. 굳이 둔다면 꺼낼 때 표시해야 하며, 넣을 때 표시하면 갱신 기회를 놓친다.
  • 거리 초기값을 0이나 작은 수로 둔다. 반드시 충분히 큰 값이어야 한다. float('inf')나 문제 제약을 넘는 큰 정수를 쓴다.
  • 음수 간선에 그대로 쓴다. 오류 없이 조용히 틀린 답이 나온다. 문제에 음수 비용이 있는지 먼저 확인한다.
  • 무방향 그래프에서 한 방향만 넣는다. 양쪽 모두 graph[a].append((b, w)), graph[b].append((a, w))로 넣어야 한다.
  • 도달 불가 정점을 처리하지 않는다. 결과에 INF가 남아 있으면 "도달 불가"라는 뜻이다. 그대로 출력하면 이상한 값이 찍힌다.

면접에서 이렇게 물어본다

  • 다익스트라가 탐욕 알고리즘인 이유를 설명해 보라.
  • 음수 간선이 있으면 왜 동작하지 않는지 반례를 들어 설명해 보라.
  • 우선순위 큐를 쓰면 복잡도가 어떻게 개선되는가.
  • 힙에서 낡은 항목을 걸러내는 방법과 그것이 필요한 이유는 무엇인가.
  • BFS로 최단 거리를 구할 수 있는 조건은 무엇인가.
  • 벨만-포드, 플로이드-워셜과 다익스트라를 어떤 기준으로 선택하겠는가.
  • 최단 거리뿐 아니라 실제 경로를 구하려면 무엇을 추가해야 하는가.
알고리즘 문서 전체 보기

이 개념, 내 코드에서는 어떻게 물어볼까요

데브핏(DevFit)에 채용공고 URL을 붙여 넣고 레포지토리를 연결하면, 내가 쓴 커밋에서 예상 질문과 어필 포인트를 만듭니다. 하루 10회까지 무료입니다. 합불 판정이나 지원자 랭킹은 만들지 않고, 최종 판단은 사람이 합니다.

공고 넣고 시작하기