153 lines
4.4 KiB
Plaintext
153 lines
4.4 KiB
Plaintext
import "./dynamic" for Tuple
|
|
import "./trait" for Comparable
|
|
import "./sort" for Cmp, Sort
|
|
import "./set" for Set
|
|
|
|
var Edge = Tuple.create("Edge", ["v1", "v2", "dist"])
|
|
|
|
// One vertex of the graph, complete with mappings to neighboring vertices
|
|
class Vertex is Comparable {
|
|
static map { __map } // maps a name to its corresponding Vertex
|
|
|
|
construct new(name) {
|
|
_name = name
|
|
_dist = Num.maxSafeInteger // assumed to be infinity
|
|
_prev = null
|
|
_neighbors = {}
|
|
if (!__map) __map = {}
|
|
__map[name] = this
|
|
}
|
|
|
|
name { _name }
|
|
dist { _dist }
|
|
dist=(d) { _dist = d }
|
|
prev { _prev }
|
|
prev=(v) { _prev = v }
|
|
neighbors { _neighbors }
|
|
|
|
printPath() {
|
|
if (this == _prev) {
|
|
System.write(_name)
|
|
} else if (!_prev) {
|
|
System.write("%(_name)(unreached)")
|
|
} else {
|
|
_prev.printPath()
|
|
System.write(" -> %(_name)(%(_dist))")
|
|
}
|
|
}
|
|
|
|
compare(other) {
|
|
if (_dist == other.dist) return Cmp.string.call(_name, other.name)
|
|
return Cmp.num.call(_dist, other.dist)
|
|
}
|
|
|
|
toString { "(%(_name), %(_dist))" }
|
|
}
|
|
|
|
class Graph {
|
|
construct new(edges, directed, showAllPaths) {
|
|
_graph = {}
|
|
// one pass to find all vertices
|
|
for (e in edges) {
|
|
if (!_graph.containsKey(e.v1)) _graph[e.v1] = Vertex.new(e.v1)
|
|
if (!_graph.containsKey(e.v2)) _graph[e.v2] = Vertex.new(e.v2)
|
|
}
|
|
|
|
// another pass to set neighboring vertices
|
|
for (e in edges) {
|
|
_graph[e.v1].neighbors[_graph[e.v2].name] = e.dist
|
|
// also do this for an undirected graph if applicable
|
|
if (!directed) _graph[e.v2].neighbors[_graph[e.v1].name] = e.dist
|
|
}
|
|
_showAllPaths = showAllPaths
|
|
_directed = directed
|
|
}
|
|
|
|
// Runs dijkstra using a specified source vertex
|
|
dijkstra(startName) {
|
|
if (!_graph.containsKey(startName)) {
|
|
System.print("Graph doesn't contain start vertex '%(startName)'")
|
|
return
|
|
}
|
|
var source = _graph[startName]
|
|
var q = Set.new()
|
|
|
|
// set-up vertices
|
|
for (v in _graph.values) {
|
|
v.prev = (v == source) ? source : null
|
|
v.dist = (v == source) ? 0 : Num.maxSafeInteger
|
|
q.add(v.name)
|
|
}
|
|
dijkstra_(q)
|
|
}
|
|
|
|
// Implementation of dijkstra's algorithm using a (simulated) tree set
|
|
dijkstra_(q) {
|
|
while (!q.isEmpty) {
|
|
var qq = q.toList
|
|
Sort.heap(qq)
|
|
// vertex with shortest distance (first iteration will return source)
|
|
var u = Vertex.map[qq[0]]
|
|
q.remove(qq[0])
|
|
// if distance is infinite we can ignore 'u'
|
|
// (and any other remaining vertices) since they are unreachable
|
|
if (u.dist == Num.maxSafeInteger) break
|
|
|
|
// look at distances to each neighbor
|
|
for (a in u.neighbors) {
|
|
var v = Vertex.map[a.key] // the neighbor in this iteration
|
|
var alternateDist = u.dist + a.value
|
|
if (alternateDist < v.dist) { // shorter path to neighbor found
|
|
v.dist = alternateDist
|
|
v.prev = u
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Prints the path from the source to every vertex
|
|
// (output order is not guaranteed)
|
|
printAllPaths_() {
|
|
for (v in _graph.values) {
|
|
v.printPath()
|
|
System.print()
|
|
}
|
|
System.print()
|
|
}
|
|
|
|
// Prints a path from the source to the specified vertex
|
|
printPath(endName) {
|
|
if (!_graph.containsKey(endName)) {
|
|
System.print("Graph doesn't contain end vertex '%(endName)'")
|
|
return
|
|
}
|
|
System.write(_directed ? "Directed : " : "Undirected : ")
|
|
_graph[endName].printPath()
|
|
System.print()
|
|
if (_showAllPaths) printAllPaths_() else System.print()
|
|
}
|
|
}
|
|
|
|
var GRAPH = [
|
|
Edge.new("a", "b", 7),
|
|
Edge.new("a", "c", 9),
|
|
Edge.new("a", "f", 14),
|
|
Edge.new("b", "c", 10),
|
|
Edge.new("b", "d", 15),
|
|
Edge.new("c", "d", 11),
|
|
Edge.new("c", "f", 2),
|
|
Edge.new("d", "e", 6),
|
|
Edge.new("e", "f", 9)
|
|
]
|
|
|
|
var START = "a"
|
|
var END = "e"
|
|
|
|
var g = Graph.new(GRAPH, true, false) // directed
|
|
g.dijkstra(START)
|
|
g.printPath(END)
|
|
|
|
g = Graph.new(GRAPH, false, false) // undirected
|
|
g.dijkstra(START)
|
|
g.printPath(END)
|