49 lines
1.1 KiB
Crystal
49 lines
1.1 KiB
Crystal
class Graph(A)
|
|
def initialize
|
|
@edges = {} of A => Set({A, Int32})
|
|
end
|
|
|
|
def add_edge(f, t, c = 1)
|
|
@edges[f] ||= Set({A, Int32}).new
|
|
@edges[f] << {t, c}
|
|
end
|
|
|
|
def add_biedge(f, t, c = 1)
|
|
add_edge(f, t, c)
|
|
add_edge(t, f, c)
|
|
end
|
|
|
|
def find_path(f, t)
|
|
visited = Set(A).new
|
|
candidates = Set { f }
|
|
distances = {f => 0}
|
|
prev = {} of A => A
|
|
|
|
while !candidates.empty?
|
|
candidate = candidates.min_by { |c| distances[c] }
|
|
break if candidate == t
|
|
visited << candidate
|
|
candidates.delete candidate
|
|
dist = distances[candidate]
|
|
|
|
@edges.fetch(candidate, Set({A, Int32}).new).each do |e|
|
|
node, cost = e
|
|
new_dist = dist + cost
|
|
candidates << node unless visited.includes? node
|
|
next if (old_dist = distances[node]?) && old_dist < new_dist
|
|
distances[node] = new_dist
|
|
prev[node] = candidate
|
|
end
|
|
end
|
|
|
|
backtrack = t
|
|
path = [t] of A
|
|
while backtrack != f
|
|
return nil unless prev_bt = prev[backtrack]?
|
|
path << prev_bt
|
|
backtrack = prev_bt
|
|
end
|
|
{path.reverse!, distances[t]}
|
|
end
|
|
end
|