Skip to content
Browse files

implement DirectedAdjacencyList and topological_sort; simplify Adjace…

…ncyList#connect to be unidirectional
  • Loading branch information...
1 parent 1891880 commit f8553f4645f427e015ed6a1a908e261d62867aee @look committed Feb 1, 2012
Showing with 58 additions and 14 deletions.
  1. +29 −7 lib/graph.rb
  2. +29 −7 tests/graph_test.rb
View
36 lib/graph.rb
@@ -5,15 +5,12 @@ module Graph
class AdjacencyList
def initialize
- @adjacencies = {}
+ @adjacencies = Hash.new { |hash, key| hash[key] = [] }
end
- def connect(vertex, *vertexes)
- if @adjacencies[vertex]
- @adjacencies[vertex] += vertexes
- else
- @adjacencies[vertex] = vertexes
- end
+ def connect(u, v)
+ @adjacencies[u] << v
+ @adjacencies[v] << u
end
def vertices
@@ -23,6 +20,24 @@ def vertices
def adjacencies(vertex)
@adjacencies[vertex]
end
+
+ def inspect
+ @adjacencies.inspect
+ end
+ end
+
+ class DirectedAdjacencyList < AdjacencyList
+ def connect(source, target=nil)
+ if @adjacencies[source]
+ @adjacencies[source] << target
+ else
+ if target.nil?
+ @adjacencies[source] = []
+ else
+ @adjacencies[source] = [target]
+ end
+ end
+ end
end
def breadth_first_search(graph, start_node)
@@ -153,5 +168,12 @@ def dfs_visit(graph, vertex, dfs_result)
dfs_result.black(vertex)
end
+
+ def topological_sort(graph)
+ dfs_result = depth_first_search(graph)
+
+ # this relies on Ruby 1.9's hashes maintaining insertion order
+ return dfs_result.finished.keys.reverse
+ end
end
end
View
36 tests/graph_test.rb
@@ -5,18 +5,33 @@ class GraphTest < Test::Unit::TestCase
include CSBS::Graph
def setup
-
# foo - bar - blah - yoyodyne
# | | |
# baz ---+ meh
@graph = AdjacencyList.new
- @graph.connect(:foo, :bar, :baz)
- @graph.connect(:bar, :blah, :foo, :baz)
- @graph.connect(:baz, :foo, :bar)
- @graph.connect(:blah, :bar, :yoyodyne, :meh)
- @graph.connect(:meh, :blah)
- @graph.connect(:yoyodyne, :blah)
+ @graph.connect(:foo, :bar)
+ @graph.connect(:foo, :baz)
+ @graph.connect(:bar, :blah)
+ @graph.connect(:bar, :baz)
+ @graph.connect(:blah, :yoyodyne)
+ @graph.connect(:blah, :meh)
+
+ # From Cormen Figure 23.7, "Professor Bumstead topologically sorts his clothing when getting dressed."
+ # The order of the connect calls is set to match the discovery times of the example.
+ @dag = DirectedAdjacencyList.new
+ @dag.connect(:shirt, :tie)
+ @dag.connect(:shirt, :belt)
+ @dag.connect(:tie, :jacket)
+ @dag.connect(:jacket)
+ @dag.connect(:belt, :jacket)
+ @dag.connect(:watch)
+ @dag.connect(:undershorts, :pants)
+ @dag.connect(:undershorts, :shoes)
+ @dag.connect(:pants, :belt)
+ @dag.connect(:pants, :shoes)
+ @dag.connect(:shoes)
+ @dag.connect(:socks, :shoes)
end
def test_breadth_first_search
@@ -67,6 +82,8 @@ def test_depth_first_search
# and AdjacencyList is implemented with a Hash. However, Ruby 1.9
# hashes maintain insertion order, making the first node
# discovered the first node inserted.
+
+ puts @graph.inspect
dfs_result = depth_first_search(@graph)
@@ -84,4 +101,9 @@ def test_depth_first_search
assert_equal(7, dfs_result.finished[:meh])
assert_equal(10, dfs_result.finished[:baz])
end
+
+ def test_topological_sort
+ expected = [:socks, :undershorts, :pants, :shoes, :watch, :shirt, :belt, :tie, :jacket]
+ assert_equal(expected, topological_sort(@dag))
+ end
end

0 comments on commit f8553f4

Please sign in to comment.
Something went wrong with that request. Please try again.