// version 1.1.51 import java.util.TreeSet class Edge(val v1: String, val v2: String, val dist: Int) /** One vertex of the graph, complete with mappings to neighbouring vertices */ class Vertex(val name: String) : Comparable { var dist = Int.MAX_VALUE // MAX_VALUE assumed to be infinity var previous: Vertex? = null val neighbours = HashMap() fun printPath() { if (this == previous) { print(name) } else if (previous == null) { print("$name(unreached)") } else { previous!!.printPath() print(" -> $name($dist)") } } override fun compareTo(other: Vertex): Int { if (dist == other.dist) return name.compareTo(other.name) return dist.compareTo(other.dist) } override fun toString() = "($name, $dist)" } class Graph( val edges: List, val directed: Boolean, val showAllPaths: Boolean = false ) { // mapping of vertex names to Vertex objects, built from a set of Edges private val graph = HashMap(edges.size) init { // one pass to find all vertices for (e in edges) { if (!graph.containsKey(e.v1)) graph.put(e.v1, Vertex(e.v1)) if (!graph.containsKey(e.v2)) graph.put(e.v2, Vertex(e.v2)) } // another pass to set neighbouring vertices for (e in edges) { graph[e.v1]!!.neighbours.put(graph[e.v2]!!, e.dist) // also do this for an undirected graph if applicable if (!directed) graph[e.v2]!!.neighbours.put(graph[e.v1]!!, e.dist) } } /** Runs dijkstra using a specified source vertex */ fun dijkstra(startName: String) { if (!graph.containsKey(startName)) { println("Graph doesn't contain start vertex '$startName'") return } val source = graph[startName] val q = TreeSet() // set-up vertices for (v in graph.values) { v.previous = if (v == source) source else null v.dist = if (v == source) 0 else Int.MAX_VALUE q.add(v) } dijkstra(q) } /** Implementation of dijkstra's algorithm using a binary heap */ private fun dijkstra(q: TreeSet) { while (!q.isEmpty()) { // vertex with shortest distance (first iteration will return source) val u = q.pollFirst() // if distance is infinite we can ignore 'u' (and any other remaining vertices) // since they are unreachable if (u.dist == Int.MAX_VALUE) break //look at distances to each neighbour for (a in u.neighbours) { val v = a.key // the neighbour in this iteration val alternateDist = u.dist + a.value if (alternateDist < v.dist) { // shorter path to neighbour found q.remove(v) v.dist = alternateDist v.previous = u q.add(v) } } } } /** Prints a path from the source to the specified vertex */ fun printPath(endName: String) { if (!graph.containsKey(endName)) { println("Graph doesn't contain end vertex '$endName'") return } print(if (directed) "Directed : " else "Undirected : ") graph[endName]!!.printPath() println() if (showAllPaths) printAllPaths() else println() } /** Prints the path from the source to every vertex (output order is not guaranteed) */ private fun printAllPaths() { for (v in graph.values) { v.printPath() println() } println() } } val GRAPH = listOf( Edge("a", "b", 7), Edge("a", "c", 9), Edge("a", "f", 14), Edge("b", "c", 10), Edge("b", "d", 15), Edge("c", "d", 11), Edge("c", "f", 2), Edge("d", "e", 6), Edge("e", "f", 9) ) const val START = "a" const val END = "e" fun main(args: Array) { with (Graph(GRAPH, true)) { // directed dijkstra(START) printPath(END) } with (Graph(GRAPH, false)) { // undirected dijkstra(START) printPath(END) } }