* Dijkstra's algorithm for weighted shortest path. * Time complexity: O((V + E) log V) with a binary heap.
(
source: GraphSource<any>,
startVertex: Vertex<any, any>,
targetId: ElementId | undefined,
targetCondition: Condition | undefined,
direction: Direction,
edgeLabels: readonly string[],
maxDepth: number,
weightProperty: string,
)
| 5179 | * Time complexity: O((V + E) log V) with a binary heap. |
| 5180 | */ |
| 5181 | private dijkstra( |
| 5182 | source: GraphSource<any>, |
| 5183 | startVertex: Vertex<any, any>, |
| 5184 | targetId: ElementId | undefined, |
| 5185 | targetCondition: Condition | undefined, |
| 5186 | direction: Direction, |
| 5187 | edgeLabels: readonly string[], |
| 5188 | maxDepth: number, |
| 5189 | weightProperty: string, |
| 5190 | ): ShortestPathResult | null { |
| 5191 | // Early termination: check if start is target |
| 5192 | if (this.isTarget(startVertex, targetId, targetCondition)) { |
| 5193 | return { vertices: [startVertex], edges: [], length: 0, weight: 0 }; |
| 5194 | } |
| 5195 | |
| 5196 | // Dijkstra data structures |
| 5197 | const distances = new Map<ElementId, number>([[startVertex.id, 0]]); |
| 5198 | const previous = new Map<ElementId, { vertex: Vertex<any, any>; edge: Edge<any, any> } | null>([ |
| 5199 | [startVertex.id, null], |
| 5200 | ]); |
| 5201 | |
| 5202 | // Priority queue using binary min-heap for O(log V) operations |
| 5203 | const pq = new MinHeap<{ vertex: Vertex<any, any>; distance: number }>( |
| 5204 | (a, b) => a.distance - b.distance, |
| 5205 | ); |
| 5206 | pq.push({ vertex: startVertex, distance: 0 }); |
| 5207 | |
| 5208 | let iterations = 0; |
| 5209 | // Safety limit: prevent DoS with hard cap independent of maxDepth |
| 5210 | const maxIterations = Math.min(maxDepth * 1000, 100000); |
| 5211 | |
| 5212 | while (pq.size > 0 && iterations < maxIterations) { |
| 5213 | iterations++; |
| 5214 | |
| 5215 | // Get vertex with minimum distance (O(log V) operation) |
| 5216 | const current = pq.pop(); |
| 5217 | if (!current) break; |
| 5218 | |
| 5219 | const { vertex: currentVertex, distance: currentDistance } = current; |
| 5220 | |
| 5221 | // Skip if we've already processed this with a shorter distance |
| 5222 | const storedDistance = distances.get(currentVertex.id); |
| 5223 | if (storedDistance !== undefined && storedDistance < currentDistance) { |
| 5224 | continue; |
| 5225 | } |
| 5226 | |
| 5227 | // Check if we've reached the target |
| 5228 | if (this.isTarget(currentVertex, targetId, targetCondition)) { |
| 5229 | // Reconstruct path |
| 5230 | return this.reconstructPath(currentVertex, previous, currentDistance); |
| 5231 | } |
| 5232 | |
| 5233 | // Get neighbors |
| 5234 | const neighbors = this.getNeighbors(source, currentVertex, direction, edgeLabels); |
| 5235 | |
| 5236 | for (const { vertex: neighbor, edge } of neighbors) { |
| 5237 | // Get edge weight (default to 1 if not found) |
| 5238 | // Note: Missing or non-numeric weights default to 1 for unweighted behavior |
no test coverage detected