다익스트라 알고리즘(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)에 관심이 많고,
이것저것 생각나는 것들을 글로 정리하는 것을 좋아합니다.
기술로 사회문제를 해결하는 시빅해킹(Civic Hacking)에 관심이 많고,
이것저것 생각나는 것들을 글로 정리하는 것을 좋아합니다.
'자료구조/알고리즘' 시리즈의 다른 이야기들