자바스크립트 코딩테스트 강좌, 플로이드-워셜

작성자: 조광형

작성일: 2024년 11월 26일

목차

  1. 1. 소개
  2. 2. 문제 설명
  3. 3. 플로이드-워셜 알고리즘 이해
  4. 4. 자바스크립트 구현
  5. 5. 시간 복잡도
  6. 6. 결론

1. 소개

코딩테스트에서 알고리즘 문제는 빈번하게 출제되며, 다양한 알고리즘을 이해하고 구현할 수 있는 능력이 중요합니다. 이 강좌에서는 플로이드-워셜(Floyd-Warshall) 알고리즘에 대해 다루고, 이를 자바스크립트로 구현하는 방법을 배웁니다. 플로이드-워셜 알고리즘은 그래프에서 모든 정점 쌍 간의 최단 경로를 찾는 데 유용하며, 특히 대규모 그래프에 적합합니다.

2. 문제 설명

문제: 주어진 방향 그래프에서 모든 정점 사이의 최단 경로를 구하는 알고리즘을 작성하시오. 주어진 그래프는 가중치가 있는 간선으로 구성되어 있습니다. 만약 어떤 두 정점 사이에 경로가 없다면, 최단 경로의 값은 무한대로 처리합니다.


입력:
- 정점의 수 N (1 ≤ N ≤ 100)
- 간선의 수 M (1 ≤ M ≤ 10000)
- M개의 간선 정보 (A, B, C): A에서 B로 가는 가중치 C

출력:
- 목적지 정점 간의 최단 경로 길이를 나타내는 N x N 행렬을 출력하시오.
        

3. 플로이드-워셜 알고리즘 이해

플로이드-워셜 알고리즘은 동적 프로그래밍(dynamic programming) 기법을 활용하여 모든 쌍의 최단 경로를 계산합니다. 해당 알고리즘은 다음과 같은 단계를 포함합니다:

  1. 모든 정점 쌍 (i, j) 간의 초기 거리 값을 설정합니다. 직접 연결된 정점 간은 가중치를, 연결되지 않은 경우는 무한대(INFINITY)를 설정합니다.
  2. 각 정점을 중간 정점으로 설정하여, i에서 j로 가는 최단 경로를 체크합니다. 중간 정점을 k로 두고 i -> k -> j 경로가 i -> j 경로보다 짧은지 확인합니다.
  3. 이 과정을 모든 정점에 대해 반복합니다.

이렇게 함으로써 최종적으로 모든 정점 쌍 간의 최단 경로를 구할 수 있습니다.


function floydWarshall(graph) {
  const dist = JSON.parse(JSON.stringify(graph)); // 그래프를 복사하여 거리 행렬 초기화

  const n = graph.length; // 정점 수
  for (let k = 0; k < n; k++) {
    for (let i = 0; i < n; i++) {
      for (let j = 0; j < n; j++) {
        if (dist[i][j] > dist[i][k] + dist[k][j]) {
          dist[i][j] = dist[i][k] + dist[k][j];
        }
      }
    }
  }
  return dist;
}
        

4. 자바스크립트 구현

이제 플로이드-워셜 알고리즘을 자바스크립트로 구현해보겠습니다. 먼저 입력을 처리하고, 그래프를 초기화한 다음, 최단 경로를 계산하는 함수를 작성합니다:


function initializeGraph(N, edges) {
  const graph = Array.from({ length: N }, () => Array(N).fill(Infinity));
  for (let i = 0; i < N; i++) {
    graph[i][i] = 0; // 자기 자신으로 가는 경우는 0
  }

  edges.forEach(([A, B, C]) => {
    graph[A][B] = Math.min(graph[A][B], C); // 여러 간선이 있을 경우 최소 가중치로 업데이트
  });

  return graph;
}

// 문제를 해결하는 메인 함수
function solve(N, M, edges) {
  const graph = initializeGraph(N, edges);
  const shortestPaths = floydWarshall(graph);

  console.log("최단 경로 행렬:");
  shortestPaths.forEach(row => console.log(row.join(" ")));
}
        

5. 시간 복잡도

플로이드-워셜 알고리즘의 시간 복잡도는 O(N^3)입니다. 이는 N이 정점의 수일 때 각 정점 쌍을 확인하기 위해 3중 루프를 사용하기 때문입니다. 따라서 정점 수가 적당히 작은 경우 (100 이하) 효율적으로 사용될 수 있지만, 수백 이상의 정점이 있는 경우에는 다른 알고리즘을 고려해야 합니다.

6. 결론

본 강좌에서는 플로이드-워셜 알고리즘의 이론과 자바스크립트 구현 방법을 살펴보았습니다. 알고리즘의 이해 및 코드 구현 연습을 통해 코딩 테스트에서의 문제 해결 능력을 키울 수 있습니다. 다음 번에는 더욱 다양한 알고리즘을 다루어 보도록 하겠습니다.

자바스크립트 코딩테스트 강좌, 최소 신장 트리

코딩 테스트는 프로그래밍 능력을 평가하는 좋은 방법입니다. 오늘은 최소 신장 트리(MST) 문제를 해결하는 방법에 대해 알아보겠습니다.
이 문제는 다양한 분야에서 활용되며, 특히 컴퓨터 네트워크와 관련된 문제에서 자주 등장합니다.
최소 신장 트리를 구축하는 알고리즘은 여러 개가 있지만, 특히 크루스칼 알고리즘(Kruskal’s Algorithm)프림 알고리즘(Prim’s Algorithm)이 많이 사용됩니다.

문제 설명

아래 그래프를 나타내는 간선이 주어졌을 때, 최소 신장 트리를 구하는 문제를 해결해 보겠습니다.
각 간선의 가중치가 부여되어 있으며, 전체 가중치의 합이 최소가 되도록 연결된 그래프를 찾아야 합니다.

문제 정의

    입력:
        [(1, 2, 4), (1, 3, 1), (2, 3, 2), (2, 4, 5), (3, 4, 8), (1, 4, 3)]
    출력:
        최소 신장 트리의 간선 리스트: [(1, 3, 1), (2, 3, 2), (1, 4, 3), (2, 4, 5)]
        최소 가중치의 합: 11

문제 풀이 과정

1단계: 그래프 데이터 구조 이해하기

그래프는 정점(노드)와 간선으로 이루어져 있습니다.
여기서는 간선이 튜플의 형태로 `(시작 정점, 끝 정점, 가중치)`로 주어집니다.
우리는 이 데이터를 이용해 그래프를 구성해야 합니다.

2단계: 간선 정렬하기

크루스칼 알고리즘은 먼저 간선을 가중치를 기준으로 정렬합니다.
와 함께 주어진 간선 리스트를 오름차순으로 정렬하여 최소 가중치의 간선을 선택할 수 있도록 합니다.

3단계: Union-Find 구조로 사이클 검사하기

간선을 추가할 때 사이클이 발생하는지를 확인하기 위해 Union-Find 자료구조를 사용합니다.
이 자료구조는 두 가지 주요 기능을 가지고 있습니다:

  • Find: 특정 요소가 어떤 집합에 속하는지 찾기
  • Union: 두 요소가 속하는 집합을 합치기

사이클이 발생하지 않으면 간선을 선택하고, 그렇지 않으면 간선을 무시합니다.
알고리즘을 구현하는 데 필요한 기본적인 Union-Find 클래스와 그 메서드를 정의합니다.

4단계: MST 구현하기

이제 모든 단계의 구현을 결합하여 최종적으로 MST를 구해보겠습니다.
아래는 자바스크립트로 구현한 크루스칼 알고리즘의 예시입니다.

    class UnionFind {
        constructor(size) {
            this.parent = Array.from({length: size}, (_, index) => index);
            this.rank = Array(size).fill(1);
        }

        find(node) {
            if (this.parent[node] !== node) {
                this.parent[node] = this.find(this.parent[node]);
            }
            return this.parent[node];
        }

        union(node1, node2) {
            const root1 = this.find(node1);
            const root2 = this.find(node2);
            if (root1 !== root2) {
                if (this.rank[root1] > this.rank[root2]) {
                    this.parent[root2] = root1;
                } else if (this.rank[root1] < this.rank[root2]) {
                    this.parent[root1] = root2;
                } else {
                    this.parent[root2] = root1;
                    this.rank[root1]++;
                }
            }
        }

        connected(node1, node2) {
            return this.find(node1) === this.find(node2);
        }
    }

    function kruskal(edges, numVertices) {
        edges.sort((a, b) => a[2] - b[2]); // 간선들을 가중치 기준으로 정렬
        const uf = new UnionFind(numVertices);
        const mst = [];
        let totalWeight = 0;

        for (const [u, v, weight] of edges) {
            if (!uf.connected(u - 1, v - 1)) {
                uf.union(u - 1, v - 1);
                mst.push([u, v, weight]);
                totalWeight += weight;
            }
        }

        return { mst, totalWeight };
    }

    const edges = [
        [1, 2, 4],
        [1, 3, 1],
        [2, 3, 2],
        [2, 4, 5],
        [3, 4, 8],
        [1, 4, 3]
    ];
    const numVertices = 4;

    const { mst, totalWeight } = kruskal(edges, numVertices);
    console.log("최소 신장 트리의 간선 리스트:", mst);
    console.log("최소 가중치의 합:", totalWeight);

5단계: 결과 분석하기

구현된 알고리즘을 통해 얻어진 결과는 다음과 같습니다.
최소 신장 트리의 간선 리스트 및 전체 가중치를 확인하여 알고리즘이 제대로 작동하는지 분석합니다.
결과적으로, 주어진 간선을 기준으로 최소 신장 트리를形成하는 데 성공했습니다.

  • 최소 신장 트리의 간선 리스트: [(1, 3, 1), (2, 3, 2), (1, 4, 3), (2, 4, 5)]
  • 최소 가중치의 합: 11

결론

오늘은 자바스크립트를 사용하여 최소 신장 트리 문제를 해결하는 방법을 살펴보았습니다.
주어진 간선 리스트를 통해 여러 알고리즘을 적용할 수 있으며, 이 과정에서 그래프의 특성 또한 이해할 수 있습니다.
이러한 문제를 풀어 나가면서 알고리즘의 성능 분석 및 최적화에 대한 경험을 쌓을 수 있으며,
실제 코딩 테스트에서 출제될 가능성이 높은 주제이므로 충분히 연습해보시기 바랍니다.

자바스크립트 코딩테스트 강좌, 너비 우선 탐색

1. 문제 제기

너비 우선 탐색(Breadth-First Search, BFS)은 그래프나 트리 구조에서 유용하게 사용되는 탐색 알고리즘 중 하나입니다. 본 강좌에서는 BFS를 활용하여 “최단 경로 찾기” 문제를 해결해보겠습니다.

문제: 최단 경로 찾기

주어진 그래프에서 두 노드 사이의 최단 경로를 찾는 함수를 작성하시오. 그래프는 인접 리스트 형태로 주어지며, 노드는 문자열로 표현됩니다.

입력 형식

        graph: {
            "A": ["B", "C"],
            "B": ["A", "D", "E"],
            "C": ["A", "F"],
            "D": ["B"],
            "E": ["B", "F"],
            "F": ["C", "E"]
        }
        start: "A"
        end: "F"
    

출력 형식

        ["A", "C", "F"]
    

2. 문제 분석

최단 경로를 찾기 위해 BFS를 사용하는 이유는 BFS가 한 번에 모든 이웃 노드를 탐색하기 때문에, 가장 빠른 경로를 보장하기 때문입니다. DFS(깊이 우선 탐색)와 달리 BFS는 현재 노드에 연결된 모든 노드를 먼저 탐색한 후, 다음 레벨로 이동합니다. 이 특징으로 인해 BFS는 최단 경로 탐색에 적합합니다.

3. 알고리즘 설계

다음은 BFS를 이용한 최단 경로 찾기 알고리즘의 기본 흐름입니다:

  1. 시작 노드를 큐에 추가하고 방문 표시를 합니다.
  2. 큐에서 노드를 하나 꺼내고, 그 노드의 이웃 노드를 모두 확인합니다.
  3. 이웃 노드 중 방문하지 않은 노드를 큐에 추가하고, 해당 노드의 부모 노드를 현재 노드로 설정합니다.
  4. 종료 노드를 찾기 전까지 2-3 단계를 반복합니다.
  5. 종료 노드에 도달했을 때, 부모 노드를 거꾸로 탐색하여 최단 경로를 작성합니다.

4. 코드 구현

아래는 BFS를 이용한 최단 경로 찾기 함수의 구현 코드입니다:

        function findShortestPath(graph, start, end) {
            // 큐와 방문 표시를 위한 객체
            let queue = [start];
            let visited = {};
            let parent = {};
            
            visited[start] = true;
            parent[start] = null;

            while (queue.length > 0) {
                let currentNode = queue.shift(); // 큐에서 노드 제거
                
                // 종료 노드에 도달한 경우
                if (currentNode === end) {
                    return reconstructPath(parent, start, end);
                }

                // 현재 노드의 이웃들 탐색
                for (let neighbor of graph[currentNode]) {
                    if (!visited[neighbor]) {
                        visited[neighbor] = true;
                        parent[neighbor] = currentNode; // 이웃 노드의 부모를 현재 노드로 설정
                        queue.push(neighbor); // 큐에 이웃 노드 추가
                    }
                }
            }
            return []; // 경로가 없을 경우 빈 배열 리턴
        }

        function reconstructPath(parent, start, end) {
            let path = [];
            let current = end;
            while (current !== null) {
                path.push(current);
                current = parent[current];
            }
            return path.reverse(); // 역순으로 경로 리턴
        }
    

5. 알고리즘 분석

위 코드는 최단 경로를 찾기 위해 BFS를 사용하고 있습니다. 시간 복잡도는 O(V + E)로, V는 노드의 수, E는 간선의 수를 의미합니다. 이 알고리즘은 인접 리스트를 활용하기 때문에 메모리 효율적인 장점이 있습니다.

6. 시간 복잡도와 공간 복잡도 분석

시간 복잡도는 O(V + E)로, 모든 노드와 간선을 한 번씩 탐색하기 때문에 이와 같은 복잡도가 발생합니다. 공간 복잡도 또한 O(V)로 큐와 방문 표시 배열, 부모 저장 배열 등이 노드 수에 비례하기 때문입니다.

7. 테스트 케이스

위 코드를 확인하기 위해 몇 가지 테스트 케이스를 만들어 보겠습니다.

        const graph = {
            "A": ["B", "C"],
            "B": ["A", "D", "E"],
            "C": ["A", "F"],
            "D": ["B"],
            "E": ["B", "F"],
            "F": ["C", "E"]
        };

        console.log(findShortestPath(graph, "A", "F")); // Output: ["A", "C", "F"]
        console.log(findShortestPath(graph, "B", "F")); // Output: ["B", "E", "F"]
        console.log(findShortestPath(graph, "D", "A")); // Output: ["D", "B", "A"]
        console.log(findShortestPath(graph, "A", "A")); // Output: ["A"]
    

8. 마무리

이번 강좌에서는 자바스크립트로 너비 우선 탐색(BFS)을 이용하여 최단 경로를 찾는 문제를 해결해 보았습니다. BFS는 인접 리스트와 같은 구조를 활용해 그래프를 탐색하는 유용한 방법이며, 다양한 문제에 응용될 수 있습니다. 앞으로도 알고리즘을 연습하며 더 많은 문제를 해결해 나가길 바랍니다.

9. 추가 자료

더 많은 알고리즘 문제와 해법을 학습하고 싶다면 온라인 코딩 테스트 플랫폼을 이용해 보세요. 각종 문제와 힌트 등을 통해 실력을 더욱 향상시킬 수 있습니다.

© 2023 자바스크립트 코딩테스트 강좌

자바스크립트 코딩테스트 강좌, ‘좋은 수’ 구하기

소개

프로그래밍 언어의 기본적인 이해를 바탕으로 알고리즘 문제를 해결하는 것은 코딩테스트에서 중요한 요소입니다. 특히 자바스크립트는 웹 개발 및 frontend 분야에서 필수적으로 사용되는 언어로, 많은 기업들이 자바스크립트를 활용한 문제를 출제하고 있습니다. 이번 강좌에서는 ‘좋은 수’라는 문제를 통해 자바스크립트의 활용성을 배우고, 알고리즘 문제 해결 과정을 자세히 설명하겠습니다.

문제 설명

‘좋은 수’ 문제는 주어진 수들 가운데 특정 조건을 만족하는 수를 찾는 문제입니다. 이 문제의 구체적인 내용은 다음과 같습니다:

문제: 양의 정수 배열이 주어질 때, 배열에서 중복을 제거하고, 10보다 작은 수를 모두 출력하는 함수를 작성하세요. 또한 남은 수들의 평균을 소수점 첫째 자리까지 구하여 반환하세요.

입력 및 출력 형식

입력: 양의 정수로 이루어진 배열 arr ([1, 2, 2, 3, 10, 5, 7, 15])
출력:
1. 중복을 제거한 수 중 10보다 작은 수의 리스트
2. 남은 수들의 평균 (소수점 첫째 자리까지)

예시

    입력: [1, 2, 2, 3, 10, 5, 7, 15]
    출력: 
    중복을 제거한 수 (10보다 작은 수): [1, 2, 3, 5, 7]
    평균: 3.6
    

문제 해결 접근 방법

문제를 해결하기 위해서는 배열의 중복을 제거하고, 필터링된 배열의 평균을 계산하는 단계를 따라야 합니다. 자바스크립트를 사용하여 이러한 작업을 수행하는 방법을 단계별로 살펴보겠습니다.

1단계: 중복 제거

배열에서 중복된 요소를 제거하기 위해 Set 객체를 사용할 수 있습니다. Set 객체는 자동으로 중복을 허용하지 않으므로, 이 객체를 이용하여 원하는 결과를 쉽게 얻을 수 있습니다.


const arr = [1, 2, 2, 3, 10, 5, 7, 15];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 10, 5, 7, 15]
    

2단계: 필터링

이제 중복이 제거된 배열에서 10보다 작은 수를 필터링하는 작업을 수행하겠습니다. JavaScript의 filter() 메소드를 활용하여 조건에 맞는 요소만을 추출할 수 있습니다.


const filteredArr = uniqueArr.filter(num => num < 10);
console.log(filteredArr); // [1, 2, 3, 5, 7]
    

3단계: 평균 계산

필터링된 배열의 평균을 계산하기 위해서는 reduce() 메소드를 사용할 수 있습니다. 배열의 모든 요소를 더한 후, 그 총합을 요소의 개수로 나눈 값을 구하면 평균을 얻을 수 있습니다.


const average = filteredArr.reduce((acc, num) => acc + num, 0) / filteredArr.length;
console.log(average.toFixed(1)); // 3.6
    

전체 코드

지금까지 설명한 모든 과정을 하나의 함수로 통합하여 최종 코드를 작성하겠습니다.


function goodNumbers(arr) {
    const uniqueArr = [...new Set(arr)];
    const filteredArr = uniqueArr.filter(num => num < 10);
    const average = filteredArr.reduce((acc, num) => acc + num, 0) / filteredArr.length;
    return {
        filteredNumbers: filteredArr,
        average: average.toFixed(1)
    };
}

const inputArr = [1, 2, 2, 3, 10, 5, 7, 15];
const result = goodNumbers(inputArr);
console.log(result);
    

결과

위의 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다.


{
    filteredNumbers: [1, 2, 3, 5, 7],
    average: "3.6"
}
    

최적화 및 고려할 점

위의 문제 해결 방법은 업무에 적합하게 잘 작동합니다. 하지만 대규모 데이터세트를 다루는 경우 성능에 대한 추가 고려가 필요할 수 있습니다. 예를 들어, Set을 사용하는 방법은 고유한 값을 쉽게 추출할 수 있지만, 배열의 크기가 매우 클 경우 메모리 사용량이 증가할 수 있습니다. 이러한 경우 알고리즘의 성능을 개선하기 위해 몇 가지 방법을 고려할 수 있습니다:

  • 중복 제거와 필터링을 동시에 수행하는 방법을 탐색하기.
  • 데이터 구조 선택에 따라 성능을 개선하기.

마무리

이번 강좌에서는 자바스크립트를 활용하여 ‘좋은 수’라는 문제를 해결하는 과정을 살펴보았습니다. 중복을 제거하고, 조건에 맞는 수를 필터링하며, 평균을 계산하는 일련의 단계를 통해 기본적인 알고리즘 문제 해결 능력을 향상시킬 수 있었습니다. 코딩테스트를 준비하는 과정에서 이러한 문제를 많이 풀어보며 연습하는 것이 중요합니다. 자바스크립트의 다양한 기능을 활용해 보며 더욱 깊이 있는 이해를 가져가시길 바랍니다.

도움이 되었나요?

이 강좌가 자바스크립트 코딩테스트를 준비하는 데 도움이 되었다면, 다른 알고리즘 문제도 도전해 보세요. 그 과정에서 발생하는 어려움이나 궁금한 점은 언제든지 댓글로 남겨주세요. 본인의 학습 여정을 공유하며 함께 성장할 수 있는 기회를 가지길 바랍니다.

자바스크립트 코딩테스트 강좌, 벨만-포드

안녕하세요. 오늘은 자바스크립트 코딩테스트를 준비하는 여러분을 위해 벨만-포드 알고리즘에 대해 자세히 알아보겠습니다. 벨만-포드 알고리즘은 그래프 이론에서 최단 경로를 찾기 위한 알고리즘 중 하나로, 특히 음의 가중치를 가진 간선이 있는 경우에도 적용할 수 있다는 장점이 있습니다. 이번 글에서는 벨만-포드 알고리즘의 개요, 원리, 문제 해결 과정 및 자바스크립트 구현 방법에 대해 다룰 것입니다.

1. 벨만-포드 알고리즘 개요

벨만-포드 알고리즘은 한 정점에서 다른 정점까지의 최단 경로를 찾기 위해 사용하는 그래프 알고리즘입니다. 이 알고리즘은 다음과 같은 특징을 가지고 있습니다:

  • 음의 가중치를 가진 간선이 있어도 사용할 수 있습니다.
  • 그래프에 사이클이 존재하지 않거나, 음의 사이클이 존재하지 않음을 확인할 수 있습니다.

2. 벨만-포드 알고리즘의 원리

벨만-포드 알고리즘은 다음과 같은 원리로 작동합니다:

  1. 시작 정점을 설정하고, 해당 정점의 거리 값을 0으로 초기화합니다. 다른 모든 정점의 거리 값은 무한대로 설정합니다.
  2. 모든 간선을 한 번씩 검사하여, 각 간선의 시작 정점에서 끝 정점으로 가는 최단 거리를 갱신합니다.
  3. 이 과정을 정점의 개수 – 1만큼 반복합니다. (그래프에서 최단 경로는 최대 V - 1번의 간선을 지나기 때문입니다.)
  4. 마지막으로 모든 간선을 다시 검사하여 거리 값이 업데이트되는 경우가 있으면, 음의 사이클이 존재한다고 판단합니다.

3. 문제 설명

다음과 같은 문제를 해결해 보겠습니다:

문제:
n개의 도시와 m개의 도로가 주어질 때, 각 도로는 출발 도시에서 도착 도시까지의 거리를 나타내는 가중치가 있습니다.
일반적으로, 출발 도시는 1번 도시이고, 도착 도시는 n번 도시입니다.
1번 도시에서 n번 도시까지의 최단 거리를 구하는 함수를 작성하세요. (음의 간선이 있을 수 있습니다.)

4. 문제 해결 과정

이 문제를 벨만-포드 알고리즘을 이용해 해결하는 과정을 상세히 설명하겠습니다:

4.1. 입력 데이터 구조 설계

문제를 해결하기 위한 입력 데이터를 정의해야 합니다. 우선, 도시의 개수와 도로의 개수, 각 도로 정보를 담는 구조를 설계해야 합니다. 아래와 같이 객체 배열을 사용하여 도로를 표현할 수 있습니다:


    // 도시 개수와 도로 개수
    const n = 5; // 도시의 개수
    const m = 8; // 도로의 개수

    // 도로 정보 (출발 도시, 도착 도시, 거리)
    const roads = [
        { from: 1, to: 2, weight: 4 },
        { from: 1, to: 3, weight: 2 },
        { from: 2, to: 3, weight: 5 },
        { from: 2, to: 4, weight: 10 },
        { from: 3, to: 2, weight: 1 },
        { from: 3, to: 4, weight: 3 },
        { from: 4, to: 5, weight: 3 },
        { from: 2, to: 5, weight: 12 }
    ];
    

4.2. 초기화

다음으로, 거리 값을 초기화합니다. 1번 도시의 거리는 0으로, 나머지 도시는 무한대로 설정합니다:


    const INF = Infinity; // 무한대 값
    const distance = Array(n + 1).fill(INF); // 1부터 n까지의 거리 배열
    distance[1] = 0; // 시작 도시의 거리 초기화
    

4.3. 벨만-포드 알고리즘 구현

이제 벨만-포드 알고리즘을 구현합니다. 간선을 n-1번 반복하여 거리 값을 갱신합니다:


    for (let i = 1; i < n; i++) {
        for (const road of roads) {
            if (distance[road.from] + road.weight < distance[road.to]) {
                distance[road.to] = distance[road.from] + road.weight;
            }
        }
    }
    

4.4. 음의 사이클 체크

마지막으로, 모든 간선을 다시 검사하여 음의 사이클이 있는지 확인하는 과정을 구현합니다:


    let hasNegativeCycle = false;

    for (const road of roads) {
        if (distance[road.from] + road.weight < distance[road.to]) {
            hasNegativeCycle = true; // 음의 사이클 존재
            break;
        }
    }

    if (hasNegativeCycle) {
        console.log("음의 사이클이 존재합니다.");
    } else {
        console.log("최단 거리:", distance[n]);
    }
    

5. 전체 코드

위의 모든 과정을 종합하여 완전한 자바스크립트 코드를 만들어 보겠습니다:


    function bellmanFord(n, roads) {
        const INF = Infinity;
        const distance = Array(n + 1).fill(INF);
        distance[1] = 0;

        // 벨만-포드 알고리즘
        for (let i = 1; i < n; i++) {
            for (const road of roads) {
                if (distance[road.from] + road.weight < distance[road.to]) {
                    distance[road.to] = distance[road.from] + road.weight;
                }
            }
        }

        // 음의 사이클 체크
        let hasNegativeCycle = false;
        for (const road of roads) {
            if (distance[road.from] + road.weight < distance[road.to]) {
                hasNegativeCycle = true;
                break;
            }
        }

        if (hasNegativeCycle) {
            console.log("음의 사이클이 존재합니다.");
        } else {
            console.log("최단 거리:", distance[n]);
        }
    }

    // 사용 예시
    const n = 5;
    const roads = [
        { from: 1, to: 2, weight: 4 },
        { from: 1, to: 3, weight: 2 },
        { from: 2, to: 3, weight: 5 },
        { from: 2, to: 4, weight: 10 },
        { from: 3, to: 2, weight: 1 },
        { from: 3, to: 4, weight: 3 },
        { from: 4, to: 5, weight: 3 },
        { from: 2, to: 5, weight: 12 }
    ];

    bellmanFord(n, roads);
    

6. 마무리

이번 글에서는 벨만-포드 알고리즘의 기본 개념과 원리, 문제를 해결하는 과정을 자세히 살펴보았습니다. 해당 알고리즘은 음의 가중치를 가진 간선이 있을 때 유용하며, 그래프 이론을 학습하는 데 중요한 알고리즘 중 하나입니다. 코딩테스트에서 자주 출제되는 주제니만큼, 반드시 숙지하시기 바랍니다.

앞으로도 다양한 알고리즘과 문제 풀이를 통해 실력을 쌓기를 바라며, 질문이 있으면 언제든지 댓글로 남겨주세요. 감사합니다!