-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgraph_traversal.rb
117 lines (102 loc) · 2.77 KB
/
graph_traversal.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# Depth-first search (DFS) and Breadth-first search (BFS)
# are algorithms for traversing or searching graph data structures.
# https://en.wikipedia.org/wiki/Depth-first_search
# https://en.wikipedia.org/wiki/Breadth-first_search
require './algorithms/graphs.rb'
module GraphTraversal
def self.scc(graph, node, &block)
index = 0
stack = []
mark(graph, :visited, false)
tarjan = lambda do |node|
node.index = index
node.lowlink = index
index += 1
stack.push(node)
node.visited = true
graph.find_nodes(node.neighbors).each do |neighbor|
if !neighbor.visited
tarjan.call(neighbor)
node.lowlink = [node.lowlink, neighbor.lowlink].min
elsif stack.include?(neighbor)
node.lowlink = [node.lowlink, neighbor.index].min
end
end
if node.lowlink == node.index && block
component = []
begin
element = stack.pop
component << element.name
end while element != node
block.call component.join('-')
end
end
graph.nodes.each do |node|
tarjan.call(node) unless node.visited
end
end
# DFS is based on a LIFO Queue (= Stack)
def self.dfs(graph, node, &block)
mark(graph, :visited, false)
stack = []
stack << node
traversal(graph, stack, &block)
end
# BFS is based on a FIFO Queue (= Queue)
def self.bfs(graph, node, &block)
mark(graph, :visited, false)
queue = Queue.new
queue << node
traversal(graph, queue, &block)
end
def self.mark(graph, attribute, value)
graph.nodes.each do |node|
node.send("#{attribute}=", value)
end
end
def self.traversal(graph, array, &block)
cycle = false
result = []
while !array.empty?
node = array.pop
if node.visited
cycle = true
else
result << node.name
node.visited = true
graph.find_nodes(node.neighbors).each do |neighbor|
array << neighbor
end
end
end
block.call "cycle detected" if cycle && block
return result
end
def self.test
nodes = [['a', 'b', 1],
['a', 'h', 1],
['b', 'h', 1],
['b', 'c', 1],
['c', 'd', 1],
['d', 'e', 1],
['e', 'f', 1],
['f', 'd', 1],
['f', 'c', 1],
['f', 'g', 1],
['g', 'h', 1],
['h', 'i', 1],
['i', 'g', 1],
['i', 'c', 1]]
graph = Graph.new(nodes: nodes)
puts "***** DFS *****"
result = dfs(graph, graph.find_node("a")) do |event|
puts event
end
puts result.join('-')
puts "***** BFS *****"
result = bfs(graph, graph.find_node("a")) do |event|
puts event
end
puts result.join('-')
end
end