Add and use a heaphash.
This commit is contained in:
parent
cfd4d1447a
commit
d1fc0c3241
|
@ -1,39 +1,45 @@
|
|||
require "./heaphash.cr"
|
||||
|
||||
class Graph(A)
|
||||
def initialize
|
||||
@edges = {} of A => Set({A, Int32})
|
||||
@nodes = Set(A).new
|
||||
end
|
||||
|
||||
def add_node(n)
|
||||
@edges[n] = Set({A, Int32}).new
|
||||
@nodes << n
|
||||
end
|
||||
|
||||
def add_edge(f, t, c = 1)
|
||||
@edges[f] ||= Set({A, Int32}).new
|
||||
@edges[f] << {t, c}
|
||||
@nodes << f << t
|
||||
end
|
||||
|
||||
def add_biedge(f, t, c = 1)
|
||||
add_edge(f, t, c)
|
||||
add_edge(t, f, c)
|
||||
end
|
||||
|
||||
def edges?(n)
|
||||
@edges[n]? || Set({A, Int32}).new
|
||||
end
|
||||
|
||||
def find_path(f, t)
|
||||
visited = Set(A).new
|
||||
candidates = Set { f }
|
||||
distances = {f => 0}
|
||||
distances = HeapHash { f => 0 }
|
||||
prev = {} of A => A
|
||||
dist = nil
|
||||
|
||||
while !candidates.empty?
|
||||
candidate = candidates.min_by { |c| distances[c] }
|
||||
while !distances.empty?
|
||||
candidate, dist = distances.pop
|
||||
break if candidate == t
|
||||
visited << candidate
|
||||
candidates.delete candidate
|
||||
dist = distances[candidate]
|
||||
|
||||
@edges.fetch(candidate, Set({A, Int32}).new).each do |e|
|
||||
edges?(candidate).each do |e|
|
||||
node, cost = e
|
||||
new_dist = dist + cost
|
||||
candidates << node unless visited.includes? node
|
||||
next if visited.includes? node
|
||||
next if (old_dist = distances[node]?) && old_dist < new_dist
|
||||
distances[node] = new_dist
|
||||
prev[node] = candidate
|
||||
|
@ -47,6 +53,29 @@ class Graph(A)
|
|||
path << prev_bt
|
||||
backtrack = prev_bt
|
||||
end
|
||||
{path.reverse!, distances[t]}
|
||||
{path.reverse!, dist.not_nil!}
|
||||
end
|
||||
|
||||
def topol
|
||||
edge_counts = HeapHash(A, Int32).new
|
||||
@nodes.each do |k|
|
||||
edge_counts[k] = 0
|
||||
end
|
||||
@edges.each do |k, v|
|
||||
v.each do |e|
|
||||
edge_counts[e[0]] -= 1
|
||||
end
|
||||
end
|
||||
|
||||
order = [] of A
|
||||
while !edge_counts.empty?
|
||||
k, count = edge_counts.pop
|
||||
raise "cycle in graph!" unless count == 0
|
||||
order << k
|
||||
edges?(k).each do |e|
|
||||
edge_counts[e[0]] += 1
|
||||
end
|
||||
end
|
||||
order
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ class Array(T)
|
|||
while i != 0
|
||||
j = (i-1)//2
|
||||
break if yield self[i], self[j]
|
||||
self[i], self[j] = self[j], self[i]
|
||||
swap(i,j)
|
||||
i = j
|
||||
end
|
||||
end
|
||||
|
@ -15,10 +15,10 @@ class Array(T)
|
|||
v1 = self[j1]
|
||||
v2 = self[j2]?
|
||||
if v2 && (yield v1, v2) && (yield self[i], v2)
|
||||
self[j2], self[i] = self[i], v2
|
||||
swap(i, j2)
|
||||
i = j2
|
||||
elsif yield self[i], v1
|
||||
self[j1], self[i] = self[i], v1
|
||||
swap(i, j1)
|
||||
i = j1
|
||||
else
|
||||
break
|
||||
|
@ -51,7 +51,7 @@ class Array(T)
|
|||
end
|
||||
|
||||
def heap_pop(&cmp : T,T -> Bool)
|
||||
self[0], self[size-1] = self[size-1], self[0]
|
||||
swap(0, size-1)
|
||||
v = pop
|
||||
percalate_down(0, &cmp)
|
||||
v
|
||||
|
|
65
src/advent/heaphash.cr
Normal file
65
src/advent/heaphash.cr
Normal file
|
@ -0,0 +1,65 @@
|
|||
require "./heap.cr"
|
||||
|
||||
# Inspired by Python's heapdict
|
||||
class HeapHash(K,V)
|
||||
private class Wrapper(K,V)
|
||||
property key : K
|
||||
property value : V
|
||||
property index : Int32
|
||||
|
||||
def initialize(@key, @value, @index)
|
||||
end
|
||||
end
|
||||
|
||||
private class InternalHeap(K, V) < Array(Wrapper(K,V))
|
||||
def swap(i,j)
|
||||
super(i,j)
|
||||
self[i].index = i
|
||||
self[j].index = j
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
@hash = {} of K => Wrapper(K,V)
|
||||
@heap = InternalHeap(K,V).new
|
||||
end
|
||||
|
||||
def delete(k)
|
||||
wrapper = @hash[k]
|
||||
@heap.bubble_up(wrapper.index) do |i, j|
|
||||
false
|
||||
end
|
||||
pop
|
||||
end
|
||||
|
||||
def []=(k : K, v : V)
|
||||
if @hash.has_key? k
|
||||
delete k
|
||||
end
|
||||
wrapper = Wrapper(K,V).new(k, v, @heap.size)
|
||||
@hash[k] = wrapper
|
||||
@heap.heap_push(wrapper) do |i, j|
|
||||
i.value < j.value
|
||||
end
|
||||
end
|
||||
|
||||
def [](k)
|
||||
wrapper = @hash[k]
|
||||
wrapper.value
|
||||
end
|
||||
|
||||
def []?(k)
|
||||
return nil unless wrapper = @hash[k]?
|
||||
wrapper.value
|
||||
end
|
||||
|
||||
def pop
|
||||
wrapper = @heap.heap_pop do |l, r|
|
||||
l.value < r.value
|
||||
end
|
||||
@hash.delete wrapper.key
|
||||
{wrapper.key, wrapper.value}
|
||||
end
|
||||
|
||||
delegate size, empty?, to: @heap
|
||||
end
|
Loading…
Reference in New Issue
Block a user