다익스트라의 원리: 거리 완화와 오래된 후보 건너뛰기

··설명 약 7분·작성: 해비·블로그 소개

읽기 전에 알아두면 좋아요

아래 개념이 익숙하면 본문을 바로 읽으세요. 낯선 개념만 링크에서 복습하면 됩니다.

개념과 예제는 실행하지 않아도 읽을 수 있습니다. 내 컴퓨터·터미널에서 직접 실행하는 과정은 선택 실습입니다.

과정 위치와 선택 복습 안내

과정 위치: 트리·그래프와 최단거리 · 알고리즘 개념

과정 위치는 이 글이 속한 묶음입니다. 이 단계의 모든 개념을 먼저 읽을 필요는 없습니다. 위에 연결한 개념 중 낯선 내용만 복습하세요. 문제의 조건·상태·갱신 순서는 익숙한 언어로 설명하면 됩니다.

JavaScript로 문법·실행 복습하기 (선택)

개념 글의 코드는 JavaScript 예시입니다. 문제 화면에서는 JavaScript·Python·Java·C++·C# 중 익숙한 언어의 함수를 작성하세요. 본문의 Node.js 입출력 예시는 로컬 실행을 위한 별도 안내입니다.

다익스트라는 거리 후보를 어떻게 완화하고 확정하나요?

다익스트라는 아직 처리하지 않은 거리 후보 중 가장 작은 것을 골라 이웃의 거리를 줄입니다. 모든 간선의 가중치가 0 이상이면 꺼낸 최신 거리보다 더 짧은 경로가 나중에 나타나지 않습니다.

0에서1의 직접 비용7을 2를 거치는 비용3으로 줄이는 다익스트라 예제
0→2 비용2,2→1 비용1을 이용하면 처음 발견한7을3으로 개선합니다. 발견과 확정을 구분합니다. 원본 보기 (새 탭)

0→1의 비용이 7이어도 0→2가 2, 2→1이 1이면 1까지의 거리는 3으로 줄어듭니다. 먼저 발견한 7은 확정값이 아닙니다.

적용 조건·범위: 정점 번호는 0부터 시작하며 각 간선은 [도착 정점, 가중치]입니다. 그래프는 방향 그래프이고 가중치는 비음수 정수, 거리 합은 Number의 안전한 정수 범위 안으로 제한합니다. 아래 예제에는 실행에 필요한 최소 힙 함수도 포함되어 있습니다.

본문 근거: Competitive Programmer’s Handbook — Antti Laaksonen · Princeton Algorithms — Shortest Paths 강의 자료

다익스트라 개념을 표현한 일러스트 표지

이 글에서 할 일: 거리 완화와 확정을 구분하고 오래된 힙 후보를 건너뜁니다. 선수 지식: 그래프의 인접 리스트·최소 힙.

더 짧은 경로를 발견하면 거리를 갱신합니다

dist[v]는 지금까지 발견한 v까지의 가장 짧은 거리입니다. 시작점은 0, 나머지는 Infinity에서 시작합니다. u에서 v로 가는 가중치 w를 보았을 때 dist[u]+w < dist[v]이면 더 짧은 경로를 찾은 것입니다. 이 갱신을 완화라고 합니다.

다익스트라의 거리·정점 후보 처리와 오래된7·8 후보 건너뛰기
후보는(거리,정점)입니다. 저장된 현재 거리와 다르면 처리하지 않으며 개선된3·4를 사용합니다. 원본 보기 (새 탭)
예제 그래프의 후보 처리
꺼낸 후보 거리 변화 판단
(0,0) dist[1]=7, dist[2]=2 시작점 처리
(2,2) dist[1]=3, dist[3]=8 2를 거쳐 개선
(3,1) dist[3]=4 1을 거쳐 개선
(4,3) 변화 없음 최신 후보 처리
(7,1), (8,3) 변화 없음 저장된 거리와 달라 건너뜀

표의 후보는 (거리,정점)입니다. 발견 시점에 방문 완료로 표시하면 7을 3으로 줄이는 경로를 놓칠 수 있습니다. 최소 힙에서 꺼낸 후보가 현재 dist와 같을 때 그 정점을 처리합니다.

최소 힙으로 실행하는 전체 예제

정점 번호는 0부터 시작하며 각 간선은 [도착 정점, 가중치]입니다. 그래프는 방향 그래프이고 가중치는 비음수 정수, 거리 합은 Number의 안전한 정수 범위 안으로 제한합니다. 아래 예제에는 실행에 필요한 최소 힙 함수도 포함되어 있습니다.

방향 그래프0~4에서 시작점0의 거리0·3·2·4와 도달불가 정점4
간선의 실제 비용과 누적 거리를 구분합니다. 고립 정점4의 코드 결과는 Infinity로 남습니다. 원본 보기 (새 탭)

JavaScript 예제: 시작점 0에서 각 정점까지의 최단거리를 구합니다. 앞의 후보 처리 표와 아래 코드·실행 결과를 함께 보며, 더 짧은 경로로 거리를 갱신하고 오래된 후보를 건너뛰는 과정을 살펴보세요. 직접 실행하지 않아도 설명을 따라갈 수 있습니다.

function dijkstra(graph, start) {
  // 정점은 0부터, 간선은 [도착 정점, 비음수 정수 가중치]
  // 거리 합은 Number의 안전한 정수 범위 안이라고 가정합니다.
  const heap = [];
  function push(item) {
    heap.push(item);
    let i = heap.length - 1;
    while (i > 0) {
      const p = Math.floor((i - 1) / 2);
      if (heap[p][0] <= heap[i][0]) break;
      [heap[p], heap[i]] = [heap[i], heap[p]];
      i = p;
    }
  }
  function pop() {
    const top = heap[0];
    const last = heap.pop();
    if (heap.length === 0) return top;
    heap[0] = last;
    let i = 0;
    while (2 * i + 1 < heap.length) {
      let child = 2 * i + 1;
      if (child + 1 < heap.length && heap[child + 1][0] < heap[child][0]) child++;
      if (heap[i][0] <= heap[child][0]) break;
      [heap[i], heap[child]] = [heap[child], heap[i]];
      i = child;
    }
    return top;
  }
  const dist = Array(graph.length).fill(Infinity);
  dist[start] = 0;
  push([0, start]);
  while (heap.length > 0) {
    const [cost, vertex] = pop();
    if (cost !== dist[vertex]) continue;
    for (const [next, weight] of graph[vertex]) {
      const candidate = cost + weight;
      if (candidate < dist[next]) {
        dist[next] = candidate;
        push([candidate, next]);
      }
    }
  }
  return dist;
}

const graph = [[[1, 7], [2, 2]], [[3, 1]], [[1, 1], [3, 6]], [], []];
console.log(dijkstra(graph, 0).join(','));

실행 결과

0,3,2,4,Infinity

정점 4는 시작점에서 도달할 수 없어 Infinity로 남습니다. cost !== dist[vertex]는 더 좋은 경로가 생긴 뒤 힙에 남아 있는 옛 후보를 건너뜁니다. 같은 거리에는 새 후보를 넣지 않으므로 0 가중치 순환에서도 같은 상태를 끝없이 추가하지 않습니다.

내 컴퓨터에서 직접 실행하기 — 다익스트라 예제 (선택)

코드를 직접 바꿔보고 싶은 분을 위한 안내입니다. 이 과정은 방문자 자신의 컴퓨터에서 진행하며, 글을 읽기 위해 반드시 실행할 필요는 없습니다.

준비물: Node.js와 코드 편집기인 Visual Studio Code(VS Code). Node.js는 JavaScript 파일을 컴퓨터에서 실행하는 프로그램입니다. 아직 설치하지 않았다면 두 프로그램의 공식 사이트에서 설치한 뒤 진행하세요.

  1. 컴퓨터에 연습용 폴더를 하나 만들고, VS Code에서 파일 → 폴더 열기로 그 폴더를 엽니다.
  2. 그 폴더 안에 dijkstra.js 파일을 만듭니다. 위의 JavaScript 예제 전체를 복사해 붙여넣고 저장하세요. 마지막 console.log 줄도 포함하며, 파일 이름 끝이 .js.txt가 되지 않도록 확인합니다.
  3. VS Code의 터미널 → 새 터미널을 선택합니다. 아래쪽에 열린 터미널은 명령어를 입력하는 창입니다. 다음 명령으로 Node.js 설치 여부를 확인합니다.
node --version

v로 시작하는 버전 번호가 나오면 실행할 수 있습니다. 명령을 찾을 수 없다는 메시지가 나오면 Node.js를 설치하고 VS Code를 완전히 닫았다가 다시 여세요.

터미널의 현재 위치가 방금 파일을 저장한 폴더인지 확인한 뒤, 다음 명령을 입력하고 Enter를 누릅니다. 이 명령은 JavaScript 파일 안이 아니라 터미널에 입력합니다.

node dijkstra.js

터미널에 나온 값을 위의 실행 결과와 비교하세요. Cannot find module이 나오면 파일 이름과 저장 여부, 터미널에서 연 폴더가 같은지 확인합니다.

비음수 조건이 거리 확정을 보장합니다

꺼낸 최신 거리 d보다 더 짧은 경로가 나중에 생긴다고 가정해 보겠습니다. 그 경로의 아직 처리하지 않은 첫 정점에는 앞선 완화로 d보다 작은 후보가 있어야 합니다. 최소 힙은 그 후보를 먼저 꺼냈어야 하므로 모순입니다. 이후 간선을 더 지나도 거리가 줄지 않는다는 비음수 조건이 이 논증의 핵심입니다.

최소 최신 후보d와 비음수 간선w의 거리 확정 논리
w≥0이면 이후 간선에서 거리가 줄지 않습니다. 더 작은 후보는 힙에서 먼저 선택된다는 조건과 함께 읽습니다. 원본 보기 (새 탭)

음수 간선은 이 조건을 깨뜨립니다. 일부 입력에서 우연히 맞더라도 이 코드의 정당성과 비용을 보장하지 않으므로 Bellman–Ford 등 입력에 맞는 방법을 선택합니다. 가중치가 모두 같은 경우에는 BFS도 검토할 수 있습니다.

평행 간선은 각각 완화하며 더 작은 후보가 남습니다. 무방향 그래프라면 간선을 양쪽 인접 리스트에 넣어야 합니다. 시작점이 유효한 정점인지, 숫자 범위를 넘지 않는지는 입력 단계에서 확인합니다.

정점 V개, 간선 E개일 때 이 중복 후보 방식의 이진 힙 구현은 O(V + E log(E+1)) 시간, 입력을 제외하고 O(V+E) 공간을 사용합니다. 평행 간선이 없는 일반적인 그래프에서는 흔히 O((V+E) log V)로 씁니다. 힙의 기존 항목을 줄이는 구현과 중복 후보를 넣는 구현의 공간 상한을 혼동하지 마세요.

직접 확인하고 문제에 적용하기

0→1 비용이 0이고 1→0도 0이면 왜 무한 반복하지 않을까요? 0→1 비용 7과 2의 평행 간선이 있으면 무엇이 남을까요?

가중치0 순환과 비용7·2 평행 간선의 다익스트라 자기 확인 초기 그래프
두 초기 그래프에서 후보를 언제 넣고 어떤 후보를 처리할지 직접 설명해 보세요. 답안은 비웠습니다. 원본 보기 (새 탭)
답과 이유 확인

같은 거리에는 후보를 추가하지 않고 더 작을 때만 추가하기 때문입니다. 평행 간선의 최종 dist[1]은 2이며, 힙에 남은 거리 7 후보는 꺼낼 때 건너뜁니다.

참고 자료

자료 확인일: 2026년 10월 19일.

부모를 기록한 경로 복원과 최소 힙 후보 선택을 나눕니다

최단 거리의 숫자를 구한 뒤 실제 경로도 필요하다면, 완화에 성공할 때 parent[v]=u를 함께 기록합니다. 0→1 비용 7, 0→2 비용 2, 2→1 비용 1이면 처음에는 parent[1]=0이지만 더 짧은 경로를 발견한 뒤 parent[1]=2로 바뀝니다. 목적지 1에서 2, 다시 0을 거꾸로 따라가서 순서를 뒤집으면 [0,2,1]입니다. 도달하지 못한 목적지는 먼저 거리 Infinity인지 확인하고 경로 없음으로 처리해야 하며, 부모가 없다는 이유만으로 목적지 하나를 유효 경로처럼 반환하면 안 됩니다.

parent1이0에서2로 바뀐 뒤1→2→0을 뒤집어 경로0→2→1로 복원
부모 기록은 거리 완화 때 갱신합니다. 부모를 거꾸로 따라간 순서와 실제 간선의 진행 방향을 구분합니다. 원본 보기 (새 탭)

부모 기록은 어떤 간선으로 개선했는지를 남기는 기능이고, 최소 힙은 다음 거리 후보를 빠르게 고르는 자료구조입니다. 미방문 정점을 매번 선형 순회하여 최소 후보를 골라도 부모 기록과 경로 복원을 할 수 있습니다. 따라서 거리 표에 부모를 추가한 개선과 선형 탐색을 힙으로 바꾸는 개선은 별개입니다. 현재의 비음수 가중치·엄격히 더 짧을 때만 완화·오래된 힙 후보 건너뛰기 조건은 유지하며, 부모도 그 유효한 완화 안에서만 갱신합니다. 경로를 뒤집어 읽을 때에는 정점 순서뿐 아니라 실제 간선 비용 합이 저장된 최단 거리와 같은지도 확인하세요.

이어서 공부할 개념

이 글이 도움이 되었나요?

조회 중

이어서 공부하기

체크포인트: 예제의 상태 변화를 손으로 추적하고 아래 문제에 적용하세요. 값이 없는 경우와 원소 하나인 경우도 설명해 보세요. 층별 방문 순서와 최솟값 추출을 추적하고, 다익스트라의 비음수 조건과 오래된 후보를 설명하세요.

다음 추천 · 이 개념으로 풀 문제: 코딩테스트 고급: 최소 힙으로 다익스트라 최단 경로 구하기

같은 주제의 문제에 적용하며 방금 배운 개념을 확인합니다.

권장 순서는 학습 가이드와 같습니다. 다른 순서는 접힌 참고 목록에서 확인할 수 있습니다. 읽음 표시는 숙달이나 채점 결과가 아닙니다.

원래 과정 목록 순서 (선택)

읽음 기록

읽음 — / 전체 30개 · 필수 0개

이 글을 쓴 해비

웹사이트를 제작·운영하고, 개인 서비스와 자동화·AI 도구를 만듭니다. 직접 만들며 배운 내용과 문제 해결 과정을 BlogFlow에 기록합니다.

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기