Permalink
Browse files

implement depth first search

  • Loading branch information...
1 parent 5e65255 commit 294dc053bbfe97f064647b7adff7b37d62d31a33 @look committed Feb 1, 2012
Showing with 99 additions and 0 deletions.
  1. +65 −0 lib/graph.rb
  2. +34 −0 tests/graph_test.rb
View
65 lib/graph.rb
@@ -88,5 +88,70 @@ def print_shortest_path(graph, start_node, end_node)
puts path.join(" -> ")
end
end
+
+ class DepthFirstSearchResult
+ attr_accessor :predecessor
+ attr_reader :timestamp, :color, :discovered, :finished
+
+ def initialize
+ @color = {}
+ @predecessor = {}
+ @discovered = {}
+ @finished = {}
+ @timestamp = 0
+ end
+
+ def timestamp!
+ @timestamp += 1
+ end
+
+ def white(vertex)
+ color[vertex] = :white
+ predecessor[vertex] = nil
+ end
+
+ def white?(vertex)
+ color[vertex] == :white
+ end
+
+ def gray(vertex)
+ color[vertex] = :gray
+ discovered[vertex] = timestamp!
+ end
+
+ def black(vertex)
+ color[vertex] = :black
+ finished[vertex] = timestamp!
+ end
+ end
+
+ def depth_first_search(graph)
+ dfs_result = DepthFirstSearchResult.new
+
+ graph.vertices.each do |vertex|
+ dfs_result.white(vertex)
+ end
+
+ graph.vertices.each do |vertex|
+ if dfs_result.white?(vertex)
+ dfs_visit(graph, vertex, dfs_result)
+ end
+ end
+
+ return dfs_result
+ end
+
+ def dfs_visit(graph, vertex, dfs_result)
+ dfs_result.gray(vertex)
+
+ graph.adjacencies(vertex).each do |v|
+ if dfs_result.white?(v)
+ dfs_result.predecessor[v] = vertex
+ dfs_visit(graph, v, dfs_result)
+ end
+ end
+
+ dfs_result.black(vertex)
+ end
end
end
View
34 tests/graph_test.rb
@@ -50,4 +50,38 @@ def test_shortest_path
assert_equal([:foo, :bar, :blah, :meh], path)
end
+
+ def test_depth_first_search
+ # Given @graph, the discover timestamp and finish timestamp for
+ # each vertex can be desk-checked as follows:
+ #
+ # foo bar blah yoyodyne
+ # [ 1/12 ] --- [ 2/11 ] -- [ 3/8 ] -- [ 4/5 ]
+ # | / |
+ # | / | meh
+ # | baz / [ 6/7 ]
+ # [ 9/10 ] /
+ #
+ # Note that this is implementation dependent for Ruby 1.9, because
+ # the depth_first_search function does not take a starting node,
+ # and AdjacencyList is implemented with a Hash. However, Ruby 1.9
+ # hashes maintain insertion order, making the first node
+ # discovered the first node inserted.
+
+ dfs_result = depth_first_search(@graph)
+
+ assert_equal(1, dfs_result.discovered[:foo])
+ assert_equal(2, dfs_result.discovered[:bar])
+ assert_equal(3, dfs_result.discovered[:blah])
+ assert_equal(4, dfs_result.discovered[:yoyodyne])
+ assert_equal(6, dfs_result.discovered[:meh])
+ assert_equal(9, dfs_result.discovered[:baz])
+
+ assert_equal(12, dfs_result.finished[:foo])
+ assert_equal(11, dfs_result.finished[:bar])
+ assert_equal(8, dfs_result.finished[:blah])
+ assert_equal(5, dfs_result.finished[:yoyodyne])
+ assert_equal(7, dfs_result.finished[:meh])
+ assert_equal(10, dfs_result.finished[:baz])
+ end
end

0 comments on commit 294dc05

Please sign in to comment.