80 lines
2.6 KiB
Nim
80 lines
2.6 KiB
Nim
# Dijkstra algorithm.
|
|
|
|
from algorithm import reverse
|
|
import sets
|
|
import strformat
|
|
import tables
|
|
|
|
type
|
|
Edge = tuple[src, dst: string; cost: int]
|
|
Graph = object
|
|
vertices: HashSet[string]
|
|
neighbours: Table[string, seq[tuple[dst: string, cost: float]]]
|
|
|
|
#---------------------------------------------------------------------------------------------------
|
|
|
|
proc initGraph(edges: openArray[Edge]): Graph =
|
|
## Initialize a graph from an edge list.
|
|
## Use floats for costs in order to compare to Inf value.
|
|
|
|
for (src, dst, cost) in edges:
|
|
result.vertices.incl(src)
|
|
result.vertices.incl(dst)
|
|
result.neighbours.mgetOrPut(src, @[]).add((dst, cost.toFloat))
|
|
|
|
#---------------------------------------------------------------------------------------------------
|
|
|
|
proc dijkstraPath(graph: Graph; first, last: string): seq[string] =
|
|
## Find the path from "first" to "last" which minimizes the cost.
|
|
|
|
var dist = initTable[string, float]()
|
|
var previous = initTable[string, string]()
|
|
var notSeen = graph.vertices
|
|
for vertex in graph.vertices:
|
|
dist[vertex] = Inf
|
|
dist[first] = 0
|
|
|
|
while notSeen.card > 0:
|
|
# Search vertex with minimal distance.
|
|
var vertex1: string
|
|
var mindist = Inf
|
|
for vertex in notSeen:
|
|
if dist[vertex] < mindist:
|
|
vertex1 = vertex
|
|
mindist = dist[vertex]
|
|
if vertex1 == last:
|
|
break
|
|
notSeen.excl(vertex1)
|
|
# Find shortest paths to neighbours.
|
|
for (vertex2, cost) in graph.neighbours.getOrDefault(vertex1):
|
|
if vertex2 in notSeen:
|
|
let altdist = dist[vertex1] + cost
|
|
if altdist < dist[vertex2]:
|
|
# Found a shorter path to go to vertex2.
|
|
dist[vertex2] = altdist
|
|
previous[vertex2] = vertex1 # To go to vertex2, go through vertex1.
|
|
|
|
# Build the path.
|
|
var vertex = last
|
|
while vertex.len > 0:
|
|
result.add(vertex)
|
|
vertex = previous.getOrDefault(vertex)
|
|
result.reverse()
|
|
|
|
#---------------------------------------------------------------------------------------------------
|
|
|
|
proc printPath(path: seq[string]) =
|
|
## Print a path.
|
|
stdout.write(fmt"Shortest path from '{path[0]}' to '{path[^1]}': {path[0]}")
|
|
for i in 1..path.high:
|
|
stdout.write(fmt" → {path[i]}")
|
|
stdout.write('\n')
|
|
|
|
#---------------------------------------------------------------------------------------------------
|
|
|
|
let graph = initGraph([("a", "b", 7), ("a", "c", 9), ("a", "f", 14),
|
|
("b", "c", 10), ("b", "d", 15), ("c", "d", 11),
|
|
("c", "f", 2), ("d", "e", 6), ("e", "f", 9)])
|
|
printPath(graph.dijkstraPath("a", "e"))
|
|
printPath(graph.dijkstraPath("a", "f"))
|