Skip to content

Instantly share code, notes, and snippets.

@qwzybug
Created April 17, 2014 05:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qwzybug/10954996 to your computer and use it in GitHub Desktop.
Save qwzybug/10954996 to your computer and use it in GitHub Desktop.
#!/usr/bin/ruby1.9.1 -Kw
# -*- coding: utf-8 -*-
# Dijkstra's single-source shortest path algorithm, from https://gist.github.com/roganartu/9407316#file-dijkstra-rb
class Edge
attr_accessor :src, :dst, :length
def initialize(src, dst, length = 1)
@src = src
@dst = dst
@length = length
end
end
class Graph < Array
attr_reader :edges
def initialize
@edges = {}
end
def connect(src, dst, length = 1)
unless self.include?(src)
raise ArgumentException, "No such vertex: #{src}"
end
unless self.include?(dst)
raise ArgumentException, "No such vertex: #{dst}"
end
key = [src, dst].sort
@edges[key] = length
end
def neighbors(vertex)
neighbors = []
@edges.each do |edge, length|
a, b = *edge
neighbors.push a if b == vertex
neighbors.push b if a == vertex
end
return neighbors.uniq
end
def length_between(src, dst)
key = [src, dst].sort
return @edges[key] if @edges.has_key? key
nil
end
def dijkstra(src, dst = nil)
distances = {}
previouses = {}
self.each do |vertex|
distances[vertex] = nil # Infinity
previouses[vertex] = nil
end
distances[src] = 0
vertices = self.clone
until vertices.empty?
nearest_vertex = vertices.inject do |a, b|
next b unless distances[a]
next a unless distances[b]
next a if distances[a] < distances[b]
b
end
break unless distances[nearest_vertex] # Infinity
if dst and nearest_vertex == dst
path = get_path(previouses, src, dst)
return { path: path, distance: distances[dst] }
end
neighbors = vertices.neighbors(nearest_vertex)
neighbors.each do |vertex|
alt = distances[nearest_vertex] + vertices.length_between(nearest_vertex, vertex)
if distances[vertex].nil? or alt < distances[vertex]
distances[vertex] = alt
previouses[vertex] = nearest_vertex
# decrease-key v in Q # ???
end
end
vertices.delete nearest_vertex
end
if dst
return nil
else
paths = {}
distances.each { |k, v| paths[k] = get_path(previouses, src, k) }
return { paths: paths, distances: distances }
end
end
private
def get_path(previouses, src, dest)
path = get_path_recursively(previouses, src, dest)
path.is_a?(Array) ? path.reverse : path
end
# Unroll through previouses array until we get to source
def get_path_recursively(previouses, src, dest)
return src if src == dest
raise ArgumentException, "No path from #{src} to #{dest}" if previouses[dest].nil?
[dest, get_path_recursively(previouses, src, previouses[dest])].flatten
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment