diff --git a/src/algorithms/graph/dijkstra/README.ko-KR.md b/src/algorithms/graph/dijkstra/README.ko-KR.md deleted file mode 100644 index e2595bbe23..0000000000 --- a/src/algorithms/graph/dijkstra/README.ko-KR.md +++ /dev/null @@ -1,16 +0,0 @@ -# 다익스트라 알고리즘(Dijkstra's algorithm) - -다익스트라 알고리즘은 도로 네트워크 등을 나타낼 수 있는 그래프에서 노드 간의 최단 경로를 찾는 알고리즘입니다. - -이 알고리즘은 다양한 형태로 존재합니다. 다익스트라의 원래 형태는 두 노드 간의 최단 경로를 찾았지만, 더 일반적인 형태는 단일 노드를 "소스"노드로 수정하고 그래프의 소스에서 다른 모든 노드까지의 최단 경로를 찾아 최단 경로 트리(shortest-path tree)를 생성합니다. - -![Dijkstra](https://upload.wikimedia.org/wikipedia/commons/5/57/Dijkstra_Animation.gif) - -`a`와 `b` 사이의 최단 경로를 찾는 다익스트라 알고리즘입니다. -가장 낮은 거리를 가지며 방문하지 않은 정점(vertex)를 선택하고, 이를 통해 방문하지 않은 각 이웃까지의 거리를 계산하며, 더 작은 경우 이웃의 거리를 업데이트합니다. 이웃에 대한 작업을 마치면 방문한 것으로 표시(빨간색으로 변경)합니다. - -## 참조 - -- [Wikipedia](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) -- [On YouTube by Nathaniel Fan](https://www.youtube.com/watch?v=gdmfOwyQlcI&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) -- [On YouTube by Tushar Roy](https://www.youtube.com/watch?v=lAXZGERcDf4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) diff --git a/src/algorithms/graph/dijkstra/__test__/dijkstra.test.js b/src/algorithms/graph/dijkstra/__test__/dijkstra.test.js deleted file mode 100644 index f6c5a263f9..0000000000 --- a/src/algorithms/graph/dijkstra/__test__/dijkstra.test.js +++ /dev/null @@ -1,117 +0,0 @@ -import GraphVertex from '../../../../data-structures/graph/GraphVertex'; -import GraphEdge from '../../../../data-structures/graph/GraphEdge'; -import Graph from '../../../../data-structures/graph/Graph'; -import dijkstra from '../dijkstra'; - -describe('dijkstra', () => { - it('should find minimum paths to all vertices for undirected graph', () => { - const vertexA = new GraphVertex('A'); - const vertexB = new GraphVertex('B'); - const vertexC = new GraphVertex('C'); - const vertexD = new GraphVertex('D'); - const vertexE = new GraphVertex('E'); - const vertexF = new GraphVertex('F'); - const vertexG = new GraphVertex('G'); - const vertexH = new GraphVertex('H'); - - const edgeAB = new GraphEdge(vertexA, vertexB, 4); - const edgeAE = new GraphEdge(vertexA, vertexE, 7); - const edgeAC = new GraphEdge(vertexA, vertexC, 3); - const edgeBC = new GraphEdge(vertexB, vertexC, 6); - const edgeBD = new GraphEdge(vertexB, vertexD, 5); - const edgeEC = new GraphEdge(vertexE, vertexC, 8); - const edgeED = new GraphEdge(vertexE, vertexD, 2); - const edgeDC = new GraphEdge(vertexD, vertexC, 11); - const edgeDG = new GraphEdge(vertexD, vertexG, 10); - const edgeDF = new GraphEdge(vertexD, vertexF, 2); - const edgeFG = new GraphEdge(vertexF, vertexG, 3); - const edgeEG = new GraphEdge(vertexE, vertexG, 5); - - const graph = new Graph(); - graph - .addVertex(vertexH) - .addEdge(edgeAB) - .addEdge(edgeAE) - .addEdge(edgeAC) - .addEdge(edgeBC) - .addEdge(edgeBD) - .addEdge(edgeEC) - .addEdge(edgeED) - .addEdge(edgeDC) - .addEdge(edgeDG) - .addEdge(edgeDF) - .addEdge(edgeFG) - .addEdge(edgeEG); - - const { distances, previousVertices } = dijkstra(graph, vertexA); - - expect(distances).toEqual({ - H: Infinity, - A: 0, - B: 4, - E: 7, - C: 3, - D: 9, - G: 12, - F: 11, - }); - - expect(previousVertices.F.getKey()).toBe('D'); - expect(previousVertices.D.getKey()).toBe('B'); - expect(previousVertices.B.getKey()).toBe('A'); - expect(previousVertices.G.getKey()).toBe('E'); - expect(previousVertices.C.getKey()).toBe('A'); - expect(previousVertices.A).toBeNull(); - expect(previousVertices.H).toBeNull(); - }); - - it('should find minimum paths to all vertices for directed graph with negative edge weights', () => { - const vertexS = new GraphVertex('S'); - const vertexE = new GraphVertex('E'); - const vertexA = new GraphVertex('A'); - const vertexD = new GraphVertex('D'); - const vertexB = new GraphVertex('B'); - const vertexC = new GraphVertex('C'); - const vertexH = new GraphVertex('H'); - - const edgeSE = new GraphEdge(vertexS, vertexE, 8); - const edgeSA = new GraphEdge(vertexS, vertexA, 10); - const edgeED = new GraphEdge(vertexE, vertexD, 1); - const edgeDA = new GraphEdge(vertexD, vertexA, -4); - const edgeDC = new GraphEdge(vertexD, vertexC, -1); - const edgeAC = new GraphEdge(vertexA, vertexC, 2); - const edgeCB = new GraphEdge(vertexC, vertexB, -2); - const edgeBA = new GraphEdge(vertexB, vertexA, 1); - - const graph = new Graph(true); - graph - .addVertex(vertexH) - .addEdge(edgeSE) - .addEdge(edgeSA) - .addEdge(edgeED) - .addEdge(edgeDA) - .addEdge(edgeDC) - .addEdge(edgeAC) - .addEdge(edgeCB) - .addEdge(edgeBA); - - const { distances, previousVertices } = dijkstra(graph, vertexS); - - expect(distances).toEqual({ - H: Infinity, - S: 0, - A: 5, - B: 5, - C: 7, - D: 9, - E: 8, - }); - - expect(previousVertices.H).toBeNull(); - expect(previousVertices.S).toBeNull(); - expect(previousVertices.B.getKey()).toBe('C'); - expect(previousVertices.C.getKey()).toBe('A'); - expect(previousVertices.A.getKey()).toBe('D'); - expect(previousVertices.D.getKey()).toBe('E'); - }); -}); diff --git a/src/algorithms/graph/dijkstra/dijkstra.js b/src/algorithms/graph/dijkstra/dijkstra.js index c5b47b08d3..50890974a3 100644 --- a/src/algorithms/graph/dijkstra/dijkstra.js +++ b/src/algorithms/graph/dijkstra/dijkstra.js @@ -1,80 +1,89 @@ -import PriorityQueue from '../../../data-structures/priority-queue/PriorityQueue'; - -/** - * @typedef {Object} ShortestPaths - * @property {Object} distances - shortest distances to all vertices - * @property {Object} previousVertices - shortest paths to all vertices. - */ - -/** - * Implementation of Dijkstra algorithm of finding the shortest paths to graph nodes. - * @param {Graph} graph - graph we're going to traverse. - * @param {GraphVertex} startVertex - traversal start vertex. - * @return {ShortestPaths} - */ -export default function dijkstra(graph, startVertex) { - // Init helper variables that we will need for Dijkstra algorithm. - const distances = {}; - const visitedVertices = {}; - const previousVertices = {}; - const queue = new PriorityQueue(); +class PriorityQueue { + constructor() { + this.values = []; + } - // Init all distances with infinity assuming that currently we can't reach - // any of the vertices except the start one. - graph.getAllVertices().forEach((vertex) => { - distances[vertex.getKey()] = Infinity; - previousVertices[vertex.getKey()] = null; - }); + add(value, priority) { + this.values.push({ value, priority }); + this.sort(); + } - // We are already at the startVertex so the distance to it is zero. - distances[startVertex.getKey()] = 0; + poll() { + return this.values.shift().value; + } - // Init vertices queue. - queue.add(startVertex, distances[startVertex.getKey()]); + isEmpty() { + return this.values.length === 0; + } - // Iterate over the priority queue of vertices until it is empty. - while (!queue.isEmpty()) { - // Fetch next closest vertex. - const currentVertex = queue.poll(); + hasValue(value) { + return this.values.some((item) => item.value === value); + } - // Iterate over every unvisited neighbor of the current vertex. - currentVertex.getNeighbors().forEach((neighbor) => { - // Don't visit already visited vertices. - if (!visitedVertices[neighbor.getKey()]) { - // Update distances to every neighbor from current vertex. - const edge = graph.findEdge(currentVertex, neighbor); + changePriority(value, newPriority) { + for (let item of this.values) { + if (item.value === value) { + item.priority = newPriority; + break; + } + } + this.sort(); + } - const existingDistanceToNeighbor = distances[neighbor.getKey()]; - const distanceToNeighborFromCurrent = distances[currentVertex.getKey()] + edge.weight; + sort() { + this.values.sort((a, b) => a.priority - b.priority); + } +} - // If we've found shorter path to the neighbor - update it. - if (distanceToNeighborFromCurrent < existingDistanceToNeighbor) { - distances[neighbor.getKey()] = distanceToNeighborFromCurrent; +function dijkstra(graph, start) { + const distances = {}; + const previous = {}; + const queue = new PriorityQueue(); + const visited = new Set(); - // Change priority of the neighbor in a queue since it might have became closer. - if (queue.hasValue(neighbor)) { - queue.changePriority(neighbor, distances[neighbor.getKey()]); - } + // Initialize distances + for (let vertex in graph) { + if (vertex === start) { + distances[vertex] = 0; + queue.add(vertex, 0); + } else { + distances[vertex] = Infinity; + } + previous[vertex] = null; + } - // Remember previous closest vertex. - previousVertices[neighbor.getKey()] = currentVertex; - } + while (!queue.isEmpty()) { + const currentVertex = queue.poll(); + + // ✅ Skip already visited nodes (handles duplicates safely) + if (visited.has(currentVertex)) continue; + visited.add(currentVertex); + + for (let neighbor in graph[currentVertex]) { + const distance = distances[currentVertex] + graph[currentVertex][neighbor]; + + if (distance < distances[neighbor]) { + distances[neighbor] = distance; + previous[neighbor] = currentVertex; - // Add neighbor to the queue for further visiting. if (!queue.hasValue(neighbor)) { - queue.add(neighbor, distances[neighbor.getKey()]); + queue.add(neighbor, distance); + } else { + queue.changePriority(neighbor, distance); } } - }); - - // Add current vertex to visited ones to avoid visiting it again later. - visitedVertices[currentVertex.getKey()] = currentVertex; + } } - // Return the set of shortest distances to all vertices and the set of - // shortest paths to all vertices in a graph. - return { - distances, - previousVertices, - }; + return { distances, previous }; } + +const graph = { + A: { B: 1, C: 4 }, + B: { C: 2, D: 5 }, + C: { D: 1 }, + D: {} +}; + +const result = dijkstra(graph, "A"); +console.log(result.distances); // { A: 0, B: 1, C: 3, D: 4 }