Data Structure & Algorithm
알고리즘, graph, Dijkstra
준희닷
2024. 7. 3. 18:18
다익스트라 알고리즘(Dijkstra's Algorithm)은 그래프에서 한 정점에서 다른 모든 정점으로의 최단 경로를 찾는 데 사용되는 알고리즘입니다. 이 알고리즘은 가중치가 있는 그래프에서 사용되며, 가중치가 음수가 아닌 경우에만 적용할 수 있습니다.
다익스트라 알고리즘의 개념
- 시작 정점 선택: 알고리즘은 시작 정점에서 시작합니다.
- 최단 경로 집합: 최단 경로를 확정한 정점들의 집합을 유지합니다.
- 미확정 집합: 아직 최단 경로가 확정되지 않은 정점들의 집합을 유지합니다.
- 거리 배열: 시작 정점에서 각 정점까지의 현재까지 발견된 최단 거리를 저장합니다. 초기에는 시작 정점의 거리를 0으로 설정하고 나머지 정점의 거리를 무한대로 설정합니다.
- 이웃 정점 업데이트: 현재 정점에서 인접한 정점들로의 거리를 계산하고, 더 짧은 경로를 발견하면 거리를 업데이트합니다.
- 반복: 모든 정점의 최단 경로가 확정될 때까지 이 과정을 반복합니다.
다익스트라 알고리즘의 과정
- 시작 정점을 설정하고, 시작 정점의 거리를 0으로 설정합니다.
- 현재 정점에서 인접한 모든 정점의 거리를 업데이트합니다.
- 아직 방문하지 않은 정점 중 가장 거리가 짧은 정점을 선택하여, 그 정점으로 이동합니다.
- 이 과정을 모든 정점이 방문될 때까지 반복합니다.
다익트라 메서드의 의사코드
- 이 함수는 시작 정점과 끝 정점을 받아야 합니다.
- 객체(이를 distances라고 부르겠습니다)를 만들고 각 키를 인접 리스트의 모든 정점으로 설정하되, 시작 정점은 0의 값을 가지도록 하고 나머지는 무한대의 값을 가지도록 합니다.
- distances 객체에 값을 설정한 후, 시작 정점을 제외한 각 정점을 우선순위 큐에 무한대의 우선순위로 추가합니다. 시작 정점은 우리가 시작하는 곳이므로 우선순위가 0이어야 합니다.
- previous라는 또 다른 객체를 만들고 각 키를 인접 리스트의 모든 정점으로 설정하되, 값은 null로 설정합니다.
- 우선순위 큐에 항목이 있는 동안 반복을 시작합니다.
- 우선순위 큐에서 정점을 dequeue합니다.
- 해당 정점이 끝 정점과 같으면 - 완료입니다!
- 그렇지 않으면 해당 정점의 인접 리스트의 각 값을 반복합니다.
- 시작 정점에서 해당 정점까지의 거리를 계산합니다.
- 그 거리가 현재 distances 객체에 저장된 값보다 작으면
- distances 객체를 새로운 더 짧은 거리로 업데이트합니다.
- previous 객체에 해당 정점을 포함하도록 업데이트합니다.
- 시작 노드로부터의 총 거리로 정점을 큐에 enqueue합니다.
자바스크립트로 다익스트라 알고리즘 구현
class PriorityQueue {
constructor() {
this.values = [];
}
enqueue(val, priority) {
this.values.push({ val, priority });
this.sort();
}
dequeue() {
return this.values.shift();
}
sort() {
this.values.sort((a, b) => a.priority - b.priority);
}
}
class WeightedGraph {
constructor() {
this.adjacencyList = {};
}
addVertex(vertex) {
if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = [];
}
addEdge(vertex1, vertex2, weight) {
this.adjacencyList[vertex1].push({ node: vertex2, weight });
this.adjacencyList[vertex2].push({ node: vertex1, weight });
}
Dijkstra(start, finish) {
// nodes dequeue 할 때, 가중치가 낮은 것을 먼저 뽑아 순회하기 위한 우선순위 큐 클래스
const nodes = new PriorityQueue();
// distances - 시작 정점을 기준으로 가장 짧은 거리를 저장해두는 객체
const distances = {};
// previous - 최종적으로 최단거리를 기억할 수 있는 객체
const previous = {};
// target - 우리가 실제로 방문하는 노드
let target;
// path - 최단 거리를 저장해서 반환할 배열
let path = [];
//build up initial state
for (let vertex in this.adjacencyList) {
if (vertex === start) {
distances[vertex] = 0;
nodes.enqueue(vertex, 0);
} else {
distances[vertex] = Infinity;
nodes.enqueue(vertex, Infinity);
}
previous[vertex] = null;
}
console.log('nodes.values:', nodes.values);
console.log('distances:', distances);
console.log('previous:', previous);
console.log('adjacencyList:', this.adjacencyList);
// as long as there is something to visit
while (nodes.values.length) {
target = nodes.dequeue().val;
if (target === finish) {
//WE ARE DONE
//BUILD UP PATH TO RETURN AT END
while (previous[target]) {
path.push(target);
target = previous[target];
}
break;
}
if (target || distances[target] !== Infinity) {
for (let neighbor in this.adjacencyList[target]) {
//find neighboring node
let nextNode = this.adjacencyList[target][neighbor];
//calculate new distance to neighboring node
let newDistance = distances[target] + nextNode.weight;
console.log('nextNode:', nextNode);
console.log('newDistance:', newDistance);
if (newDistance < distances[nextNode.node]) {
//updating new target distance to neighbor
distances[nextNode.node] = newDistance;
//updating previous - How we got to neighbor
previous[nextNode.node] = target;
//enqueue in priority queue with new priority
nodes.enqueue(nextNode.node, newDistance);
}
}
}
}
return path.concat(target).reverse();
}
}
var graph = new WeightedGraph();
graph.addVertex('A');
graph.addVertex('B');
graph.addVertex('C');
graph.addVertex('D');
graph.addVertex('E');
graph.addVertex('F');
graph.addEdge('A', 'B', 4);
graph.addEdge('A', 'C', 2);
graph.addEdge('B', 'E', 3);
graph.addEdge('C', 'D', 2);
graph.addEdge('C', 'F', 4);
graph.addEdge('D', 'E', 3);
graph.addEdge('D', 'F', 1);
graph.addEdge('E', 'F', 1);
console.log(graph.Dijkstra('A', 'E'));
// ["A", "C", "D", "F", "E"]
다익스트라 알고리즘의 시간 복잡도
- 우선순위 큐를 사용하지 않은 경우: (O(V^2))
- 우선순위 큐를 사용한 경우 (예: 힙 사용):
- 삽입/삭제: (O(log V))
- 총 시간 복잡도: (O((V + E) log V))
다익스트라 알고리즘의 특징
- 단일 시작점: 시작 정점에서 다른 모든 정점으로의 최단 경로를 구합니다.
- 비음수 가중치: 간선의 가중치가 음수인 경우에는 사용할 수 없습니다.
- 효율성: 우선순위 큐(힙)를 사용하면 큰 그래프에서도 비교적 효율적으로 작동합니다.
다익스트라 알고리즘은 네트워크 라우팅, 지도 서비스, 교통 시스템 등 다양한 분야에서 널리 사용됩니다.