Dijkstra's algorithm
This page uses content from Wikipedia. The original article was at Dijkstra's algorithm. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance) |
Dijkstra's algorithm, conceived by Dutch computer scientist Edsger Dijkstra in 1956 and published in 1959, is a graph search algorithm that solves the single-source shortest path problem for a graph with nonnegative edge path costs, producing a shortest path tree. This algorithm is often used in routing and as a subroutine in other graph algorithms.
For a given source vertex (node) in the graph, the algorithm finds the path with lowest cost (i.e. the shortest path) between that vertex and every other vertex. It can also be used for finding costs of shortest paths from a single vertex to a single destination vertex by stopping the algorithm once the shortest path to the destination vertex has been determined. For example, if the vertices of the graph represent cities and edge path costs represent driving distances between pairs of cities connected by a direct road, Dijkstra's algorithm can be used to find the shortest route between one city and all other cities. As a result, the shortest path first is widely used in network routing protocols, most notably IS-IS and OSPF (Open Shortest Path First).
Task:
- Implement Dijkstra's algorithm.
- Document the specific algorithm implemented. The {{trans}} template is sufficient. Otherwise add text outside of your program or add comments within your program. This is not a requirement to explain how the algorithm works, but to state which algorithm is implemented. If your code follows an external source such as the Wikipedia pseudocode, you can state that. You can state if it is Dijkstra's original algorithm or some more efficient variant. It is relevant to mention things like priority queues, heaps, and expected time complexity in big-O notation.
- Run your program with the following graph to find the shortest path between vertex "a" and vertex "e."
- Show the output of your program.
Number | Name |
---|---|
1 | a |
2 | b |
3 | c |
4 | d |
5 | e |
6 | f |
End points | Cost |
---|---|
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 |
You can use numbers or names to identify vertices in your program.
Go
<lang go>package main
import (
"bytes" "container/heap" "fmt" "io/ioutil" "math"
)
// edge struct holds the bare data needed to define a graph type edge struct {
vert1, vert2 string dist int
}
func main() {
// example data from Wikipedia article g := []edge{ {"1", "2", 7}, {"1", "3", 9}, {"1", "6", 14}, {"2", "3", 10}, {"2", "4", 15}, {"3", "4", 11}, {"3", "6", 2}, {"4", "5", 6}, {"5", "6", 9}, } path, dist, n := shortestPath("1", "5", g) fmt.Printf("WP example with %d nodes, %d edges\n", n, len(g)) fmt.Println("Shortest path: ", path) fmt.Println("Path distance: ", dist) g = extraCreditGraph() path, dist, n = shortestPath("rosetta", "code", g) fmt.Printf("\nEC example with %d nodes, %d edges\n", n, len(g)) fmt.Println("Shortest path: ", path) fmt.Println("Path distance: ", dist)
}
// node and neighbor structs hold data useful for the heap-optimized // Dijkstra's shortest path algorithm type node struct {
vert string // vertex name tent int // tentative distance prev *node // previous node in shortest path back to start done bool // true when tent and prev represent shortest path nbs []neighbor // edges from this vertex rx int // heap.Remove index
}
type neighbor struct {
nd *node // node corresponding to vertex dist int // distance to this node (from whatever node references this)
}
const maxInt = math.MaxInt32
// shortestPath runs Dijkstra's algorithm. // Inputs are names of start and end vertices, and a graph in the form // of a simple list of edges. // Output is the path found as a list of vertex names, and for convenience, // the the total path distance and number of nodes in the graph. // Output is an empty list if there is no path from start to end. func shortestPath(start, end string, graph []edge) (p []string, d, n int) {
// Setup for the algorithm constructs a linked representation // of the input graph, with fields needed by the algorithm. // In the process we take care of WP steps 1 and 2. all := make(map[string]*node) // one pass over graph to collect nodes for _, e := range graph { // WP step 1: initialize tentative distance to maxInt if all[e.vert1] == nil { all[e.vert1] = &node{vert: e.vert1, tent: maxInt} } if all[e.vert2] == nil { all[e.vert2] = &node{vert: e.vert2, tent: maxInt} } } current := all[start] // WP step 2 last := all[end] if current == nil || last == nil { return // start or end vertex not in graph } current.tent = 0 // WP step 1 // second pass to link neighbors for _, e := range graph { n1 := all[e.vert1] n2 := all[e.vert2] n1.nbs = append(n1.nbs, neighbor{n2, e.dist}) n2.nbs = append(n2.nbs, neighbor{n1, e.dist}) } // WP step 2 var unvis ndList
for { // WP step 5: check for end of path if current == last { for p = []string{end}; current.vert != start; { current = current.prev p = append(p, current.vert) } for i := (len(p) + 1) / 2; i > 0; i-- { p[i-1], p[len(p)-i] = p[len(p)-i], p[i-1] } return p, last.tent, len(all) } // WP step 3: update tentative distances to neighbors for _, nb := range current.nbs { if nd := nb.nd; !nd.done { if d := current.tent + nb.dist; d < nd.tent { if nd.prev != nil { heap.Remove(&unvis, nd.rx) } nd.tent = d nd.prev = current heap.Push(&unvis, nd) } } } // WP step 4: mark current node visited current.done = true // WP step 6: new current is node with smallest tentative distance current = heap.Pop(&unvis).(*node) if current == nil { break // no path exists } } return
}
// ndList implements container/heap type ndList []*node
func (n ndList) Len() int { return len(n) } func (n ndList) Less(i, j int) bool { return n[i].tent < n[j].tent } func (n ndList) Swap(i, j int) {
n[i], n[j] = n[j], n[i] n[i].rx = i n[j].rx = j
} func (n *ndList) Push(x interface{}) {
nd := x.(*node) nd.rx = len(*n) *n = append(*n, nd)
} func (n *ndList) Pop() interface{} {
s := *n if len(s) == 0 { return nil } last := len(s) - 1 r := s[last] *n = s[:last] return r
}
// Large graph for extra credit uses words for vertices and creates // edges where a prefix of one word matches a suffix of another. // With a an overlap of three, for example, The word "rosetta" shares // an edge with the word "eros" because the first three letters of // rosetta matches the last three letters of eros. The edge distance // is defined as the sum of the lengths of the two words. const overlap = 3
func extraCreditGraph() []edge {
b, err := ioutil.ReadFile("unixdict.txt") if err != nil { fmt.Println(err) return nil } pMap := make(map[string][]string) var total int for _, w := range bytes.Split(b, []byte{'\n'}) { if len(w) > overlap { pre := string(w[:overlap]) pMap[pre] = append(pMap[pre], string(w)) total++ } } var g []edge for _, list := range pMap { for _, word := range list { for _, link := range pMap[word[len(word)-overlap:]] { g = append(g, edge{word, link, len(word) + len(link)}) } } } return g
}</lang> Output:
WP example with 6 nodes, 9 edges Shortest path: [1 3 6 5] Path distance: 20 EC example with 21409 nodes, 246498 edges Shortest path: [rosetta eros hero erode odessa code] Path distance: 49
Java
Notes for this solution:
- The number of nodes is fixed to less than 50
- At every iteration, the next minimum distance node found by linear traversal of all nodes, which is inefficient.
<lang java>import java.io.*; import java.util.*;
class Graph {
private static final int MAXNODES = 50; private static final int INFINITY = Integer.MAX_VALUE; int n; int[][] weight = new int[MAXNODES][MAXNODES]; int[] distance = new int[MAXNODES]; int[] precede = new int[MAXNODES];
/** * Find the shortest path across the graph using Dijkstra's algorithm. */ void buildSpanningTree(int source, int destination) {
boolean[] visit = new boolean[MAXNODES];
for (int i=0 ; i<n ; i++) { distance[i] = INFINITY; precede[i] = INFINITY; } distance[source] = 0;
int current = source; while (current != destination) { int distcurr = distance[current]; int smalldist = INFINITY; int k = -1; visit[current] = true;
for (int i=0; i<n; i++) { if (visit[i]) continue;
int newdist = distcurr + weight[current][i]; if (newdist < distance[i]) { distance[i] = newdist; precede[i] = current; } if (distance[i] < smalldist) { smalldist = distance[i]; k = i; } } current = k; }
}
/** * Get the shortest path across a tree that has had its path weights * calculated. */ int[] getShortestPath(int source, int destination) {
int i = destination; int finall = 0; int[] path = new int[MAXNODES];
path[finall] = destination; finall++; while (precede[i] != source) { i = precede[i]; path[finall] = i; finall++; } path[finall] = source;
int[] result = new int[finall+1]; System.arraycopy(path, 0, result, 0, finall+1); return result;
}
/** * Print the result. */ void displayResult(int[] path) {
System.out.println("\nThe shortest path followed is : \n"); for (int i = path.length-1 ; i>0 ; i--) System.out.println("\t\t( " + path[i] + " ->" + path[i-1] + " ) with cost = " + weight[path[i]][path[i-1]]); System.out.println("For the Total Cost = " + distance[path[path.length-1]]);
}
/** * Prompt for a number. */ int getNumber(String msg) {
int ne = 0; BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
try { System.out.print("\n" + msg + " : "); ne = Integer.parseInt(in.readLine()); } catch (Exception e) { System.out.println("I/O Error"); } return ne;
}
/** * Prompt for a tree, build and display a path across it. */ void SPA() {
n = getNumber("Enter the number of nodes (Less than " + MAXNODES + ") in the matrix");
System.out.print("\nEnter the cost matrix : \n\n"); for (int i=0 ; i<n ; i++) for (int j=0 ; j<n ; j++) weight[i][j] = getNumber("Cost " + (i+1) + "--" + (j+1));
int s = getNumber("Enter the source node"); int d = getNumber("Enter the destination node");
buildSpanningTree(s, d); displayResult(getShortestPath(s, d));
}
}
public class Dijkstra {
public static void main(String args[]) {
Graph g = new Graph(); g.SPA();
}
}</lang>
Mathematica
<lang Mathematica>bd = Graph[ { "a"\[UndirectedEdge] "b", "a"\[UndirectedEdge] "c", "b"\[UndirectedEdge] "c", "b"\[UndirectedEdge] "d", "c"\[UndirectedEdge] "d", "d"\[UndirectedEdge] "e", "a"\[UndirectedEdge] "f", "c"\[UndirectedEdge] "f", "e"\[UndirectedEdge] "f" } , EdgeWeight->{7,9,10,15,11,6,14,2,9},VertexLabels->"Name", VertexLabelStyle->Directive[Black,20],ImagePadding->20]
FindShortestPath[bd, "a", "e", Method -> "Dijkstra"] -> {"a", "c", "f", "e"}</lang>
PARI/GP
Basic, inefficient implementation. Takes an n×n matrix representing distance between nodes (a 0-1 matrix if you just want to count number of steps) and a number in 1..n representing the starting node, which defaults to 1 if not given. <lang parigp>shortestPath(G, startAt=1)={ my(n=#G[,1],dist=vector(n,i,9e99),prev=dist,Q=2^n-1); dist[startAt]=0; while(Q, my(t=vecmin(vecextract(dist,Q)),u); if(t==9e99, break); for(i=1,#v,if(dist[i]==t && bittest(Q,i-1), u=i; break)); Q-=1<<(u-1); for(i=1,n, if(!G[u,i],next); my(alt=dist[u]+G[u,i]); if (alt < dist[i], dist[i]=alt; prev[i]=u; ) ) ); dist };</lang>
Ruby
(for INFINITY)
Notes for this solution:
- At every iteration, the next minimum distance node found by linear traversal of all nodes, which is inefficient.
<lang ruby>class Graph
Vertex = Struct.new(:name, :neighbours, :dist, :prev) Edge = Struct.new(:v1, :v2, :distance) class Edge def vertices; [v1, v2]; end end
def initialize(graph) @vertices = {} @edges = [] graph.each do |(v1, v2, dist)| @vertices[v1] = Vertex.new(v1, []) unless @vertices.has_key?(v1) vert1 = @vertices[v1] @vertices[v2] = Vertex.new(v2, []) unless @vertices.has_key?(v2) vert2 = @vertices[v2] vert1.neighbours << vert2 vert2.neighbours << vert1 @edges << Edge.new(vert1, vert2, dist) end @dijkstra_source = nil end attr_reader :vertices, :edges
def edge(u, v) @edges.find {|e| e.vertices == [u, v] or e.vertices == [v, u]} end
def dijkstra(source) q = vertices.values q.each {|v| v.dist = Float::INFINITY} source.dist = 0 until q.empty? u = q.min_by {|vertex| vertex.dist} break if u == Float::INFINITY q.delete(u) u.neighbours.each do |v| if q.include?(v) alt = u.dist + edge(u,v).distance if alt < v.dist v.dist = alt v.prev = u end end end end @dijkstra_source = source end
def shortest_path(source, target) dijkstra(source) unless @dijkstra_source == source path = [] u = target until u.nil? path.unshift(u) u = u.prev end path end
def to_s "#<%s vertices=%s edges=%s>" % [self.class.name, vertices.values.inspect, edges.inspect] end
end
g = Graph.new([ [:a, :b, 7],
[:a, :c, 9], [:b, :c, 10], [:b, :d, 15], [:c, :d, 11], [:d, :e, 6], [:a, :f, 14], [:c, :f, 2], [:e, :f, 9], ])
start = g.vertices[:a] stop = g.vertices[:e] path = g.shortest_path(start, stop) puts "shortest path from #{start.name} to #{stop.name} has cost #{stop.dist}:" puts path.map {|vertex| vertex.name}.join(" -> ")</lang>
output
shortest path from a to e has cost 20: a -> c -> f -> e
Tcl
Note that this code traverses the entire set of unrouted nodes at each step, as this is simpler than computing the subset that are reachable at each stage. <lang tcl>proc dijkstra {graph origin} {
# Initialize dict for {vertex distmap} $graph {
dict set dist $vertex Inf dict set path $vertex {}
} dict set dist $origin 0 dict set path $origin [list $origin]
while {[dict size $graph]} {
# Find unhandled node with least weight set d Inf dict for {uu -} $graph { if {$d > [set dd [dict get $dist $uu]]} { set u $uu set d $dd } }
# No such node; graph must be disconnected if {$d == Inf} break
# Update the weights for nodes lead to by the node we've picked dict for {v dd} [dict get $graph $u] { if {[dict exists $graph $v]} { set alt [expr {$d + $dd}] if {$alt < [dict get $dist $v]} { dict set dist $v $alt dict set path $v [list {*}[dict get $path $u] $v] } } }
# Remove chosen node from graph still to be handled dict unset graph $u
} return [list $dist $path]
}</lang> Showing the code in use: <lang tcl>proc makeUndirectedGraph arcs {
# Assume that all nodes are connected to something foreach arc $arcs {
lassign $arc v1 v2 cost dict set graph $v1 $v2 $cost dict set graph $v2 $v1 $cost
} return $graph
} set arcs {
{a b 7} {a c 9} {b c 10} {b d 15} {c d 11} {d e 6} {a f 14} {c f 2} {e f 9}
} lassign [dijkstra [makeUndirectedGraph $arcs] "a"] costs path puts "path from a to e costs [dict get $costs e]" puts "route from a to e is: [join [dict get $path e] { -> }]"</lang> Output:
path from a to e costs 20 route from a to e is: a -> c -> f -> e