abonglog

          • 소프트웨어 개발방법론

            • 로우파이 와이어프레임과 하이파이 와이어프레임
          • 자료구조 및 알고리즘

            • 다익스트라 알고리즘
            • 플로이드-워셜 알고리즘
            • 외판원 순회 문제(TSP) 를 완전 탐색 , DP로 풀어보자
            • 순열,조합과 그래프의 관계에 대해 알아보자
            • 백준 10986 - 나머지합 (모듈러 연산 , 누적합, 중복조합)
          • 함수형 자바스크립트

            • 모나드와 함께하는 함수형 프로그래밍 - Maybe 모나드
            • 복잡한 상태관리, 함수형으로 생각하며 리팩토링하기
            • 이터레이터와 이터러블, 제네레이터, 비동기 이터러블
            • 멀티패러다임 프로그래밍 서적 리뷰
            • 제네레이터를 이용해 자바스크립트의 큐 자료구조 10줄로 구현하기
            • 함수형 자바스크립트 모나드 알아보기
            • 함수형 자바스크립트의 펑터와 적용형 펑터
            • 커링 (currying) 에 대해 알아보자
            • 함수형 프로그래밍의 정의와 기초지식 및 가볍게 살펴보는 활용 예제
            • 함수형 자바스크립트 프로그래밍 학습 커리큘럼
          • 컴퓨터 공학 지식

            • 고급 프롬프트 엔지니어링을 위한 개념 정리
            • 개방형 와이파이에서도 폼 데이터는 안전할까 ?
          • 독서 노트

            • 솔로프리너의 시대 서평 리뷰
          • 인생 회고록

            • AI 에이전트를 이용해 블로그의 UI를 리디자인하며 느낀 회고 (바이브코딩, 양의 순환고리, 회고의 중요성)
          • 웹 브라우저 지식

            • 경험에 의거한 FSD (Feature Sliced Design) 구조 완전 공략
            • zustand는 어떻게 마법같이 동작할까?
            • 이번에 합성 컴포넌트를 이용하여 디자인 시스템을 만들어봤던 경험
            • 함수형 컴포넌트의 useEffect에 대한 사견, 부수효과 관점에서 다시 보기
            • 브라우저의 캐시 사용법 및 NextJS 에서 캐시를 사용하는 방법
            • NextJS 는 어떻게 이미지 최적화를 구현하는가 ?
          • introduction to algorithms

            • 이진 검색 트리 (이진 탐색 트리)
          • mostly-adequate-guide

            • Chapter 13: Monoids bring it all together [번역]
            • Chapter 12: Traversing the Stone [번역]
            • Chapter 11: Transform Again, Naturally [번역]
            • Chapter 10: Applicative Functors [번역]
            • Chapter 09: Monadic Onions [번역]
            • Chapter 08: Tupperware [번역]
            • Chapter 07: Hindley-Milner and Me [번역]
            • Chapter 06: Example Application [번역]
            • Chapter 05: Coding by Composing [번역]
            • Chapter 04: Currying [번역]
            • Chapter 03: Pure Happiness with Pure Functions [번역]
            • Chapter 02: First Class Functions [번역]
            • Chapter 01: What Ever Are We Doing? [번역]
          • Zero to One

            • [InklingMe : Slice-1 : Action] 첫 번째 애자일 이터레이션을 가진 후 진행한 액션 후기
            • [InklignMe : Slice-1 : Recap] 조그만 기능 대비 쓸데없이 복잡한 엔지니어링 과정을 거쳤던 과정 회고
            • 2번의 프로젝트 관리 실패로 배운 1인 개발의 씁쓸한 회고록
            • Zero to one 시리즈를 시작하며
          abonglog logoabonglog
          다익스트라 알고리즘  의 썸네일

          다익스트라 알고리즘

          자료구조 및 알고리즘
          프로필 이미지
          yonghyeun5/9/2025, 5:48:00 AM

          다익스트라 알고리즘

          다익스트라 알고리즘은 어떤 그래프 G에 대해 i,j 노드를 잇는 최단 경로를 구하는 알고리즘이다.

          그래프 자료구조는 실생활에서 가장 많이 접할 수 있으며 최단 경로를 구하는 행위는 자주 일어나기 때문에 상당히 중요하다.

          해당 알고리즘을 공부하게 된 계기는 백준 - 1916 최소비용 구하기 문제를 풀다가 시간복잡도 문제에 허덕여 공부하게 되었다.

          해당 개념은 이전 네트워크를 공부하며 라우팅 단계에서 접한적은 있지만 실제 코드로 구현해보는 것은 처음이기에 여러 블로그 자료들을 통해 공부하도록 하자 !

          플로이드 - 워셜 알고리즘과의 차이점

          나무위키상에서는 플로이드 - 워셜 알고리즘과 시간 복잡도가 동일하다고 나와있지만 실제로는 조금 달랐다.

          플로이드 - 워셜 알고리즘은 그래프 상 모든 노드에 대해 모든 노드 간의 최단 경로 를 구하지만 다익스트라 알고리즘은 출발지로부터 도달 가능한 노드까지의 최단 경로 만을 구한다.

          이에 데이터 개수가 매우 커지면 시간 복잡도 자체는 동일해질 수 있지만 실제 연산 횟수는 다익스트라 알고리즘이 더 적다.

          다익스트라 알고리즘 개념 설명

          note

          매우 길고 장황하게 적어놨었지만 시간이 지나고보니 코드가 너무 별로인거 같아서 다시 작성한다.

          공부에 사용할 그래프공부에 사용할 그래프

          다익스트라 알고리즘에서 사용 할 자료구조를 풀기 전에 개념이 어떻게 흘러가는지 차례대로 생각해보자

          위 예시에서 A 부터 F까지 가는 최단 경로를 구하고 싶다고 가정해보자

          그렇다면 그 최단 경로는 A 부터 F 까지 바로 가거나, 어떤 노드를 경유하거나 두 가지중 하나이다.

          이 때 경유하는 어떤 노드의 집합을 K 라고 정의해보자

          이전에 풀이했던 플로이드 워셜 알고리즘에선 이 집합 K 는 그래프상의 모든 노드들이 존재했다면, 다익스트라 알고리즘은 출발지에서 도달 가능한 노드에 대해서만 존재한다.

          그 이유는 플로이드 워셜 알고리즘의 관심사는 그래프 상에 존재하는 모든 간선들의 최단 경로였다면 다익스트라 알고리즘은 출발지에서 도착지까지의 최단 경로에만 관심이 있기 때문이다.

          다익스트라 알고리즘은 다음과 같은 과정을 거친다.

          1. 그래프 상 경유지를 거치지 않고 어떤 그래프까지 가는 간선의 가중치를 담은 2차원 배열을 생성한다. (weights)
          2. 출발지로부터 도달 가능한 노드까지의 최단 경로를 담을 배열 (shortestPath) 생성 (만약 도달이 불가능하다면 노드는 infinity로 설정)
          3. 출발지로부터 도달 가능한 노드들을 담을 우선순위 큐 (queue) 생성. 이 큐는 shortestPath 값이 작을 수록, 즉 가장 최단 경로인 노드를 우선적으로 dequeue 한다.
          4. 최단 경로를 이미 구한 노드들을 표시할 배열을 생성한다. (visited)
          5. queue 에 출발지를 담고 queue 가 모두 빌 때 까지 5,6,7 과정을 반복한다.
          6. queue 에서 값을 하나 뺀다.(currentNode) 이 값은 항상 shortestPath 에서 가장 값이 작은 노드가 반환된다. 이 값은 이미 최단 경로임이 보장되었으니 visited[currentNode] 를 true 로 표시한다.
          7. 현재 있는 노드 (currentNode) 와 인접한 (도달 가능한) 모든 nextNode 에 대해 shortestPath[nextNode] 값을 업데이트 한다. 이 값은 이전에 구해둔 shortestPath[nextNode] 와 shortestPath[currentNode] + weights[currentNode][nextNode] 중 최소 경로로 업데이트 된다.
          8. nextNode 를 queue 에 담는다.

          다익스트라 알고리즘을 다시 풀면서 느낀게 제일 중요한게 두가지 개념인거 같다.

          하나는 출발지로부터 도달 가능한 노드에 대해서만 탐욕적으로 탐색 한다는 것, 두 번째는 우선순위 큐를 통해 반복 과정에서 큐에서 뽑은 노드가 매번 최단 경로가 이미 결정된 노드 라는 것이다.

          해답 코드

          밑 코드는 백준 - 1916 최소비용 구하기 에 대한 정답 코드이다.

          예제 입 출력
          // 입력
          5
          8
          1 2 2
          1 3 3
          1 4 1
          1 5 10
          2 4 2
          3 4 1
          3 5 1
          4 5 3
          1 5
          // 출력 
          4
          다익스트라 알고리즘
          const fs = require("fs");
          const path = require("path");
           
          const filePath =
            process.platform === "linux"
              ? "/dev/stdin"
              : path.join(__dirname, "input.txt");
           
          const inputs = fs.readFileSync(filePath).toString().trim().split("\n");
          const [N, M] = inputs.slice(0, 2).map(Number);
          const notes = inputs.slice(2, M + 2).map((line) => line.split(" ").map(Number));
          const [start, end] = inputs[M + 2].split(" ").map(Number);
           
          // N 개의 노드에서 W[i][j] 는 i 에서 j 까지 가는 간선의 가중치
          const weights = Array.from({ length: N + 1 }, () =>
            Array(N + 1).fill(Infinity)
          );
           
          notes.forEach(([start, end, weight]) => {
            // 동일한 start 부터 end 까지 가는 간선이 여러개일 수 있으니
            // 최소 가중치로 업데이트
            weights[start][end] = Math.min(weights[start][end], weight);
          });
           
          for (let i = 1; i <= N; i++) {
            //  자기 자신으로 가는 간선은 모두 0으로 초기화
            weights[i][i] = 0;
          }
           
          // start 부터 N 개의 모든 노드까지 가는 최단 경로를 담을 배열
          // 바텀업 과정을 통해 최단 경로로 업데이트 예정
          const shortestPath = [...weights[start]];
          // shortestPath 를 바라보는 우선순위 큐 객체 생성
          const queue = {
            queue: [],
            size: 0,
            enqueue(node) {
              this.queue.push(node);
              this.size++;
           
              if (this.size === 1) {
                return;
              }
           
              // min heap 성질을 만족하도록 정렬
              let currentIndex = this.size - 1;
              let parentIndex = Math.floor((currentIndex - 1) / 2);
           
              while (
                parentIndex >= 0 &&
                shortestPath[this.queue[parentIndex]] >
                  shortestPath[this.queue[currentIndex]]
              ) {
                const temp = this.queue[currentIndex];
                this.queue[currentIndex] = this.queue[parentIndex];
                this.queue[parentIndex] = temp;
           
                currentIndex = parentIndex;
                parentIndex = Math.floor((currentIndex - 1) / 2);
              }
            },
           
            dequeue() {
              if (this.size === 0) {
                return null;
              }
           
              if (this.size === 1) {
                this.size--;
                return this.queue.pop();
              }
           
              const root = this.queue[0];
              this.queue[0] = this.queue.pop();
              this.size--;
           
              // min heap 성질을 만족하도록 정렬
              let currentIndex = 0;
              let leftChildIndex = currentIndex * 2 + 1;
              let rightChildIndex = currentIndex * 2 + 2;
              while (true) {
                let minIndex = currentIndex;
           
                if (
                  shortestPath[this.queue[leftChildIndex]] <
                  shortestPath[this.queue[minIndex]]
                ) {
                  minIndex = leftChildIndex;
                }
           
                if (
                  shortestPath[this.queue[rightChildIndex]] <
                  shortestPath[this.queue[minIndex]]
                ) {
                  minIndex = rightChildIndex;
                }
           
                if (minIndex === currentIndex) {
                  break;
                }
           
                const temp = this.queue[currentIndex];
                this.queue[currentIndex] = this.queue[minIndex];
                this.queue[minIndex] = temp;
                currentIndex = minIndex;
                leftChildIndex = currentIndex * 2 + 1;
                rightChildIndex = currentIndex * 2 + 2;
              }
           
              return root;
            },
          };
           
          const visited = Array(N + 1).fill(false);
           
          // queue 가 모두 빌 때 까지 shortestPath 업데이트
          queue.enqueue(start);
           
          while (queue.size > 0) {
            const currentNode = queue.dequeue();
           
            if (visited[currentNode]) {
              continue;
            }
            visited[currentNode] = true;
           
            // 다음에 방문할 노드들을 큐에 담는다.
            for (let nextNode = 1; nextNode <= N; nextNode++) {
              // 현재 노드와 인접하지 않은 노드는 고려하지 않는다.
              if (weights[currentNode][nextNode] === Infinity) {
                continue;
              }
              // 이미 방문한 노드는 고려하지 않는다.
              if (visited[nextNode]) {
                continue;
              }
           
              // start 노드부터 nextNode 까지 가는 최단 경로를 업데이트
              shortestPath[nextNode] = Math.min(
                shortestPath[nextNode],
                shortestPath[currentNode] + weights[currentNode][nextNode]
              );
           
              queue.enqueue(nextNode);
            }
          }
           
          console.log(shortestPath[end]); // 4

          회고

          다익스트라 알고리즘을 이해하는데도 애를 먹었는데 우선순위 큐를 구현하는데도 애를 꽤나 먹었다.

          갸갸갹 너무 헷갈려

          • 다익스트라 알고리즘
            • 플로이드 - 워셜 알고리즘과의 차이점
          • 다익스트라 알고리즘 개념 설명
          • 해답 코드
          • 회고

          abonglog

          공부한 내용을 기록하고 함께 성장하고 싶어 만든 두 번째 블로그입니다.
          주로 웹개발과 관련된 내용을 포스팅합니다.

          Githubttddcc119@naver.com

          © 2026 abonglog All rights reserved.

          이전 포스트플로이드-워셜 알고리즘