RosettaCodeData/Task/Dijkstras-algorithm/Nim/dijkstras-algorithm.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"))