Add and use a heaphash.
This commit is contained in:
parent
cfd4d1447a
commit
d1fc0c3241
|
@ -1,39 +1,45 @@
|
||||||
|
require "./heaphash.cr"
|
||||||
|
|
||||||
class Graph(A)
|
class Graph(A)
|
||||||
def initialize
|
def initialize
|
||||||
@edges = {} of A => Set({A, Int32})
|
@edges = {} of A => Set({A, Int32})
|
||||||
|
@nodes = Set(A).new
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_node(n)
|
def add_node(n)
|
||||||
@edges[n] = Set({A, Int32}).new
|
@nodes << n
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_edge(f, t, c = 1)
|
def add_edge(f, t, c = 1)
|
||||||
@edges[f] ||= Set({A, Int32}).new
|
@edges[f] ||= Set({A, Int32}).new
|
||||||
@edges[f] << {t, c}
|
@edges[f] << {t, c}
|
||||||
|
@nodes << f << t
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_biedge(f, t, c = 1)
|
def add_biedge(f, t, c = 1)
|
||||||
add_edge(f, t, c)
|
add_edge(f, t, c)
|
||||||
add_edge(t, f, c)
|
add_edge(t, f, c)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edges?(n)
|
||||||
|
@edges[n]? || Set({A, Int32}).new
|
||||||
|
end
|
||||||
|
|
||||||
def find_path(f, t)
|
def find_path(f, t)
|
||||||
visited = Set(A).new
|
visited = Set(A).new
|
||||||
candidates = Set { f }
|
distances = HeapHash { f => 0 }
|
||||||
distances = {f => 0}
|
|
||||||
prev = {} of A => A
|
prev = {} of A => A
|
||||||
|
dist = nil
|
||||||
|
|
||||||
while !candidates.empty?
|
while !distances.empty?
|
||||||
candidate = candidates.min_by { |c| distances[c] }
|
candidate, dist = distances.pop
|
||||||
break if candidate == t
|
break if candidate == t
|
||||||
visited << candidate
|
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
|
node, cost = e
|
||||||
new_dist = dist + cost
|
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
|
next if (old_dist = distances[node]?) && old_dist < new_dist
|
||||||
distances[node] = new_dist
|
distances[node] = new_dist
|
||||||
prev[node] = candidate
|
prev[node] = candidate
|
||||||
|
@ -47,6 +53,29 @@ class Graph(A)
|
||||||
path << prev_bt
|
path << prev_bt
|
||||||
backtrack = prev_bt
|
backtrack = prev_bt
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,7 @@ class Array(T)
|
||||||
while i != 0
|
while i != 0
|
||||||
j = (i-1)//2
|
j = (i-1)//2
|
||||||
break if yield self[i], self[j]
|
break if yield self[i], self[j]
|
||||||
self[i], self[j] = self[j], self[i]
|
swap(i,j)
|
||||||
i = j
|
i = j
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -15,10 +15,10 @@ class Array(T)
|
||||||
v1 = self[j1]
|
v1 = self[j1]
|
||||||
v2 = self[j2]?
|
v2 = self[j2]?
|
||||||
if v2 && (yield v1, v2) && (yield self[i], v2)
|
if v2 && (yield v1, v2) && (yield self[i], v2)
|
||||||
self[j2], self[i] = self[i], v2
|
swap(i, j2)
|
||||||
i = j2
|
i = j2
|
||||||
elsif yield self[i], v1
|
elsif yield self[i], v1
|
||||||
self[j1], self[i] = self[i], v1
|
swap(i, j1)
|
||||||
i = j1
|
i = j1
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
|
@ -51,7 +51,7 @@ class Array(T)
|
||||||
end
|
end
|
||||||
|
|
||||||
def heap_pop(&cmp : T,T -> Bool)
|
def heap_pop(&cmp : T,T -> Bool)
|
||||||
self[0], self[size-1] = self[size-1], self[0]
|
swap(0, size-1)
|
||||||
v = pop
|
v = pop
|
||||||
percalate_down(0, &cmp)
|
percalate_down(0, &cmp)
|
||||||
v
|
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