http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
http://en.algoritmy.net/article/44220/Tarjans-algorithm
強く接続されたコンポーネントに対する Tarjan のアルゴリズムの私の Ruby バージョンでは、このバグを理解できません。Kosaraju-Sharir アルゴリズムが動作するようになり、Ruby の Tarjan アルゴリズムがいくつかのグラフで動作します。しかし、接続されるべき2つのコンポーネントが接続されていません---「10」と「11、12、9」
入力ファイルはこの有向グラフです: http://algs4.cs.princeton.edu/42directed/tinyDG.txt
expected: [["1"], ["0", "2", "3", "4", "5"], ["10", "11", "12", "9"], ["6", "8"], ["7"]]
got: [["1"], ["0", "2", "3", "4", "5"], ["10"], ["11", "12", "9"], ["6", "8"], ["7"]]
単一のコンポーネントを作成しようとするこの最後のループでは、「10」(スタックの最後のアイテム) から始まりますが、現在の頂点 (「親」) も「10」です! これにより、ループが別のコンポーネントとして「10」を切り捨てられます。スタックの最新のアイテムが親ノードと同じなのはなぜですか? ["12"、"11"、"9"、そして "10"] を収集した後、コンポーネントの END にのみ "10" が表示されることを期待します。「10」が最後ではなく最初に表示されるため、この問題が発生します。どうすれば修正できますか?
begin
last_stack_item = stack.pop
component << last_stack_item.name
end while last_stack_item != parent # we're back at the root
私のRubyコード:
# Tarjan's algorithm to find all strongly connected components (SCCs)
def scc_tarjan
index = 0 # numbers nodes consecutively in the order discovered
stack, scc, vertices = [], [], []
# create new Struct, if not already defined
if Struct::const_defined?("TarjanVertex")
Struct.const_get("TarjanVertex")
else
Struct.new("TarjanVertex", :name, :index, :lowlink)
end
adj_lists.each do |v|
# -1 means vertex is unvisited
vertex = Struct::TarjanVertex.new(v.name, -1, -1)
vertices << vertex # array of all TarjanVertex objects in graph
end
vertices.each do |vertex|
tarjan_dfs(vertex, scc, stack, index, vertices) if vertex.index == -1
end
# return nested array of all SCCs in graph
scc
end
def tarjan_dfs(parent, scc, stack, index, vertices)
# Set depth index for vertex to smallest unused index
parent.index = index
# lowlink is roughly the smallest index of any node known to be reachable from the vertex
parent.lowlink = index
index += 1
stack << parent
# loop through all vertices connected to parent
adj_vertices(parent.name, adj_lists).each do |adj_vertex|
# since adj_vertices returns array of strings,
# must convert to TarjanVertex objects
child = vertices.select {|v| v.name == adj_vertex}.first
if child.index == -1 # if child vertex not yet visited
tarjan_dfs(child, scc, stack, index, vertices) # recurse on child
# change parent's lowlink to smaller lowlink of parent and child)
parent.lowlink = [parent.lowlink, child.lowlink].min
# vertex points to earlier (already visited) one in stack,
# with lower index. thus it's the current SCC
elsif stack.include?(child)
parent.lowlink = [parent.lowlink, child.index].min
end
end
# if a vertex's lowlink = its index here, this # cannot go any lower.
# vertex MUST be root of the SCC.
if parent.lowlink == parent.index
component = [] # a single SCC
# pop off entire SCC, one vertex at a time
begin
last_stack_item = stack.pop
component << last_stack_item.name
end while last_stack_item != parent # we're back at the root
scc << component.sort # done with a single SCC
end
end