다익스트라 알고리즘(Dijkstra Algorithm)이란 무엇인가

2020.03.13

메인

다익스트라 알고리즘(Dijkstra Algorithm)이란?

가중치가 있는 그래프의 최단 경로는 BFS(BFS 문서 참고)로는 찾을 수 없다.
BFS는 간선의 숫자가 적은 경로를 찾을 뿐, 간선의 가중치는 고려하지 않기 때문이다.

다익스트라 알고리즘은 가중치를 고려한 최단경로를 계산할 수 있는 알고리즘이다.

구현 - JavaScript

과정

비용이 가장 싼 경로를 찾는다고 가정한다.

(1) 가장 싼 정점을 찾는다.

(2) 이 정점의 이웃 정점에 대해 현재 가격보다 더 싼 경로가 있는지 확인 -> 있으면 가격을 수정

정점에 도달하는 가장 싼 가격을 찾는 것이 이 알고리즘의 핵심 아이디어이다.

(3) 모든 정점에 대해 (1), (2)를 반복

(4) 최종경로를 계산

도착 정점에서 거꾸로 부모정점을 찾아감으로써 찾는다.

/**
 * 가중 그래프 클래스
 * findLowestPath메서드가 다익스트라 알고리즘을 이용하여 최단거리를 탐색
 */
class WeightedGraph {
  constructor() {
    this.vertices = [];
    this.adjList = {};
    this.costs = {};
    this.parents = {};
    this.processed = new Set();
  }

  addVertex(v) {
    this.vertices.push(v);
    this.adjList[v] = {};
  }

  addEdge(start, end, weight) {
    this.adjList[start][end] = weight;
  }

  findLowestPath() {
    let node = this._findLowestAndUnProcessedNode();
    let cost, neighbors, newCost;
    while (node) {
      cost = this.costs[node];
      neighbors = this.adjList[node];

      for (const n in neighbors) {
        newCost = cost + neighbors[n];
        if (this.costs[n] > newCost) {
          this.costs[n] = newCost;
          this.parents[n] = node;
        }
      }

      this.processed.add(node);
      node = this._findLowestAndUnProcessedNode();
    }
  }

  _findLowestAndUnProcessedNode() {
    let lowestCost = Infinity;
    let lowestCostNode, cost;
    this.vertices.forEach(node => {
      cost = this.costs[node];
      if (cost < lowestCost && !this.processed.has(node)) {
        lowestCost = cost;
        lowestCostNode = node;
      }
    });

    return lowestCostNode;
  }

  // 디버깅용 헬퍼 함수
  toString() {
    let result = "### adjList ###\n";
    for (const key in this.adjList) {
      result += `${key}: `;
      for (const key2 in this.adjList[key]) {
        result += `->${key2}(${Object.values(this.adjList[key][key2])}) `;
      }
      result += "\n";
    }

    result += `\n\n### cost ###\n`;
    for (const key in this.costs) {
      result += `${key}: ${this.costs[key]}\n`;
    }

    result += `\n\n### parents ###\n`;
    for (const key in this.parents) {
      result += `${key}: ${this.parents[key]}\n`;
    }

    result += `\n\n### processed ###\n`;
    result += Array.from(this.processed).join(", ");
    return result;
  }
}

// Test
it("가중그래프와 다익스트라 알고리즘은 잘 동작한다.", () => {
  const graph = new WeightedGraph();

  graph.addVertex("START");
  graph.addVertex("A");
  graph.addEdge("START", "A", 6);
  graph.addVertex("B");
  graph.addEdge("START", "B", 2);
  graph.addEdge("B", "A", 3);
  graph.addEdge("B", "END", 5);
  graph.addEdge("A", "END", 1);

  graph.costs["A"] = 6;
  graph.costs["B"] = 2;
  graph.costs["END"] = Infinity;

  graph.parents["A"] = "START";
  graph.parents["B"] = "START";
  graph.parents["END"] = null;

  graph.findLowestPath();

  expect(graph.parents["END"]).toEqual("A");
  expect(graph.parents["A"]).toEqual("B");
  expect(graph.parents["B"]).toEqual("START");
});

한계

그래프에 사이클(cycle)이 있는 경우에는 사용할 수 없다.

방향 그래프(Directed Graph)면서 비사이클 그래프(acyclic graph)인 경우만 사용할 수 있다.

간선에 음의 가중치가 있는 경우에는 사용할 수 없다.

양의 가중치만 있는 그래프에만 사용 가능하다.

음의 가중치를 가진 그래프에서 최단 경로를 찾기 위해 벨만-포드 알고리즘(Bleman-Ford Algorithm)을 사용할 수 있다.

사용 영역

  • 가중치 있는 그래프의 최단 경로 찾기
  • 아디트야 바르가바, 『Hello Coding 그림으로 개념을 이해하는 알고리즘』, 김도형, 한빛미디어(2017)
글쓴이쿠스, Qus
직업은 소프트웨어 개발자.
기술로 사회문제를 해결하는 시빅해킹(Civic Hacking)에 관심이 많고,
이것저것 생각나는 것들을 글로 정리하는 것을 좋아합니다.