Kosaraju: Difference between revisions
SqrtNegInf (talk | contribs) (Added Perl example) |
Alextretyak (talk | contribs) m (→{{header|11l}}: Void) |
||
(38 intermediate revisions by 15 users not shown) | |||
Line 1:
{{
{{wikipedia|Graph}}
[[Category:Algorithm]]
<br>
Kosaraju's algorithm (also known as the Kosaraju–Sharir algorithm) is a linear time algorithm to find the strongly connected components of a directed graph. Aho, Hopcroft and Ullman credit it to an unpublished paper from 1978 by S. Rao Kosaraju. The same algorithm was independently discovered by Micha Sharir and published by him in 1981. It makes use of the fact that the transpose graph (the same graph with the direction of every edge reversed) has exactly the same strongly connected components as the original graph.
<br>
For this task consider the directed graph with these connections:
0 -> 1
1 -> 2
2 -> 0
3 -> 1, 3 -> 2, 3 -> 4
4 -> 3, 4 -> 5
5 -> 2, 5 -> 6
6 -> 5
7 -> 4, 7 -> 6, 7 -> 7
And report the kosaraju strongly connected component for each node.
<br>
;References:
* The article on [[wp:Kosaraju's_algorithm|Wikipedia]].
=={{header|11l}}==
{{trans|Python}}
<syntaxhighlight lang="11l">F kosaraju(g)
V size = g.len
V vis = [0B] * size
V l = [0] * size
V x = size
V t = [[Int]()] * size
F visit(Int u) -> Void
I !@vis[u]
@vis[u] = 1B
L(v) @g[u]
@visit(v)
@t[v] [+]= u
@x--
@l[@x] = u
L(u) 0 .< g.len
visit(u)
V c = [0] * size
F assign(Int u, root) -> Void
I @vis[u]
@vis[u] = 0B
@c[u] = root
L(v) @t[u]
@assign(v, root)
L(u) l
assign(u, u)
R c
V g = [[1], [2], [0], [1, 2, 4], [3, 5], [2, 6], [5], [4, 6, 7]]
print(kosaraju(g))</syntaxhighlight>
{{out}}
<pre>
[0, 0, 0, 3, 3, 5, 5, 7]
</pre>
=={{header|C sharp|C#}}==
<syntaxhighlight lang="csharp">using System;
using System.Collections.Generic;
class Node
{
public enum Colors
{
Black, White, Gray
}
public Colors color { get; set; }
public int N { get; }
public Node(int n)
{
N = n;
color = Colors.White;
}
}
class Graph
{
public HashSet<Node> V { get; }
public Dictionary<Node, HashSet<Node>> Adj { get; }
/// <summary>
/// Kosaraju's strongly connected components algorithm
/// </summary>
public void Kosaraju()
{
var L = new HashSet<Node>();
Action<Node> Visit = null;
Visit = (u) =>
{
if (u.color == Node.Colors.White)
{
u.color = Node.Colors.Gray;
foreach (var v in Adj[u])
Visit(v);
L.Add(u);
}
};
Action<Node, Node> Assign = null;
Assign = (u, root) =>
{
if (u.color != Node.Colors.Black)
{
if (u == root)
Console.Write("SCC: ");
Console.Write(u.N + " ");
u.color = Node.Colors.Black;
foreach (var v in Adj[u])
Assign(v, root);
if (u == root)
Console.WriteLine();
}
};
foreach (var u in V)
Visit(u);
foreach (var u in L)
Assign(u, u);
}
}</syntaxhighlight>
=={{header|C++}}==
{{trans|D}}
<
#include <iostream>
#include <ostream>
Line 96 ⟶ 224:
return 0;
}</
{{out}}
<pre>[0, 0, 0, 3, 3, 5, 5, 7]</pre>
=={{header|D}}==
{{trans|Kotlin}} (mostly) with output like {{trans|Go}}
<
import std.stdio;
Line 242 ⟶ 296:
void main() {
writeln(kosaraju(g));
}</
{{out}}
<pre>[0, 0, 0, 3, 3, 5, 5, 7]</pre>
=={{header|Delphi}}==
{{libheader| System.SysUtils}}
{{Trans|Go}}
<syntaxhighlight lang="delphi">
program KosarajuApp;
uses
System.SysUtils;
type
TKosaraju = TArray<TArray<Integer>>;
var
g: TKosaraju;
procedure Init();
begin
SetLength(g, 8);
g[0] := [1];
g[1] := [2];
g[2] := [0];
g[3] := [1, 2, 4];
g[4] := [3, 5];
g[5] := [2, 6];
g[6] := [5];
g[7] := [4, 6, 7];
end;
procedure Println(vector: TArray<Integer>);
var
i: Integer;
begin
write('[');
if (Length(vector) > 0) then
for i := 0 to High(vector) do
begin
write(vector[i]);
if (i < high(vector)) then
write(', ');
end;
writeln(']');
end;
function Kosaraju(g: TKosaraju): TArray<Integer>;
var
vis: TArray<Boolean>;
L, c: TArray<Integer>;
x: Integer;
t: TArray<TArray<Integer>>;
Visit: TProc<Integer>;
u: Integer;
Assign: TProc<Integer, Integer>;
begin
// 1. For each vertex u of the graph, mark u as unvisited. Let L be empty.
SetLength(vis, Length(g));
SetLength(L, Length(g));
x := Length(L);
// index for filling L in reverse order
SetLength(t, Length(g)); // transpose graph
// 2. recursive subroutine:
Visit := procedure(u: Integer)
begin
if (not vis[u]) then
begin
vis[u] := true;
for var v in g[u] do
begin
Visit(v);
t[v] := concat(t[v], [u]);
// construct transpose
end;
dec(x);
L[x] := u;
end;
end;
// 2. For each vertex u of the graph do Visit(u)
for u := 0 to High(g) do
begin
Visit(u);
end;
SetLength(c, Length(g)); // result, the component assignment
// 3: recursive subroutine:
Assign := procedure(u, root: Integer)
begin
if vis[u] then
// repurpose vis to mean "unassigned"
begin
vis[u] := false;
c[u] := root;
for var v in t[u] do
Assign(v, root);
end;
end;
// 3: For each element u of L in order, do Assign(u,u)
for u in L do
Assign(u, u);
Result := c;
end;
begin
Init;
Println(Kosaraju(g));
end.</syntaxhighlight>
{{out}}
<pre>[0, 0, 0, 3, 3, 5, 5, 7]</pre>
=={{header|Go}}==
<
import "fmt"
Line 306 ⟶ 478:
}
return c
}</
{{out}}
<pre>
[0 0 0 3 3 5 5 7]
</pre>
=={{header|J}}==
Implementation:
<syntaxhighlight lang="j">kosaraju=: {{
coerase([cocurrent)cocreate''
visit=: {{
if.y{unvisited do.
unvisited=: 0 y} unvisited
visit y{::out
L=: y,L
end.
}}"0
assign=: {{
if._1=y{assigned do.
assigned=: x y} assigned
x&assign y{::in
end.
}}"0
out=: y
in=: <@I.|:y e.S:0~i.#y
unvisited=: 1#~#y
assigned=: _1#~#y
L=: i.0
visit"0 i.#y
assign~L
assigned
}}</syntaxhighlight>
Task example (tree representation: each value here is the index of the parent node):
<syntaxhighlight lang="j"> kosaraju 1;2;0;1 2 4;3 5;2 6;5;4 6 7
0 0 0 3 3 5 5 7</syntaxhighlight>
Alternatively, we could represent the result as a graph of the same type as our argument graph. The implementation of <tt>visit</tt> remains the same:
<syntaxhighlight lang="j">kosarajud=: {{
coerase([cocurrent)cocreate''
visit=: {{
if.y{unvisited do.
unvisited=: 0 y} unvisited
visit y{::out
L=: y,L
end.
}}"0
assign=: {{
if.-.y e.;assigned do.
assigned=: (y,L:0~x{assigned) x} assigned
x&assign y{::in
end.
}}"0
out=: y
in=: <@I.|:y e.S:0~i.#y
unvisited=: 1#~#y
assigned=: a:#~#y
L=: i.0
visit"0 i.#y
assign~L
assigned
}}
kosarajud 1;2;0;1 2 4;3 5;2 6;5;4 6 7
┌─────┬┬┬───┬┬───┬┬─┐
│0 2 1│││3 4││5 6││7│
└─────┴┴┴───┴┴───┴┴─┘
</syntaxhighlight>
But it would probably be simpler to convert the result from the tree representation to our directed graph representation:
<syntaxhighlight lang="j"> ((<@I.@e."1 0)i.@#) 0 0 0 3 3 5 5 7
┌─────┬┬┬───┬┬───┬┬─┐
│0 1 2│││3 4││5 6││7│
└─────┴┴┴───┴┴───┴┴─┘</syntaxhighlight>
=={{header|Java}}==
{{trans|Kotlin}}
Output is like Go instead of what Kotlin outputs.
<
import java.util.Arrays;
import java.util.List;
Line 401 ⟶ 646:
System.out.println(output);
}
}</
{{out}}
<pre>[0, 0, 0, 3, 3, 5, 5, 7]</pre>
=={{header|jq}}==
{{trans|Go}}
<syntaxhighlight lang="jq">
# Fill an array of the specified length with the input value
def dimension($n): . as $in | [range(0;$n) | $in];
# $graph should be an adjacency-list graph with IO==0
def korasaju($graph):
($graph|length) as $length
| def init: {
vis: (false | dimension($length)), # visited
L: [], # for an array of $length integers
t: ([]|dimension($length)), # transposed graph
x: $length # index
};
# input: {vis, L, t, x, t}
def visit($u):
if .vis[$u] | not
then .vis[$u] = true
| reduce ($graph[$u][]) as $v (.;
visit($v)
| .t[$v] += [$u] )
| .x -= 1
| .L[.x] = $u
else .
end ;
# input: {vis, t, c}
def assign($u; $root):
if .vis[$u]
then .vis[$u] = false
| .c[$u] = $root
| reduce .t[$u][] as $v (.; assign($v; $root))
else .
end ;
# For each vertex u of the graph, mark u as unvisited.
init
# For each vertex u of the graph do visit(u)
| reduce range(0;$length) as $u (.; visit($u))
| .c = (null|dimension($length))
# For each element u of L in order, do assign(u, u)
| reduce .L[] as $u (.; assign($u; $u) )
| .c ;
# An example adjacency list using IO==1
def g: [
[1],
[2],
[0],
[1, 2, 4],
[3, 5],
[2, 6],
[5],
[4, 6, 7]
];
korasaju(g)</syntaxhighlight>
{{out}}
With the jq program above in korasaju.jq, the invocation `jq -nc -f korasaju.jq` produces:
<syntaxhighlight lang="sh">
[0,0,0,3,3,5,5,7]
</syntaxhighlight>
=={{header|Julia}}==
Line 409 ⟶ 721:
{{trans|Go}}
<
# 1. For each vertex u of the graph, mark u as unvisited. Let L be empty.
vis = falses(length(g))
Line 451 ⟶ 763:
g = [[2], [3], [1], [2, 3, 5], [4, 6], [3, 7], [6], [5, 7, 8]]
println(korasaju(g))</
{{out}}
<pre>[1, 1, 1, 4, 4, 6, 6, 8]</pre>
=={{header|K}}==
{{works with|ngn/k}}
<syntaxhighlight lang=K>F:{[g] / graph
n: #g / number of vertices
v::&n / visited?
L::!0 / dfs order
V: {[g;x] $[v x;;[v[x]:1;o[g]'g x;L::x,L]];}[g]
V'!n / Visit
G: @[n#,!0;g;,;!n] / transposed graph
c::n#-1 / assigned components
A: {[G;x;y] $[-1=c x;[c[x]:y;G[x]o[G]'y];]}[G]'
A'/2#,L / Assign
.=c}</syntaxhighlight>
<syntaxhighlight lang=K> F(1;2;0;1 2 4;3 5;2 6;5;4 6 7)
(0 1 2
3 4
5 6
,7)</syntaxhighlight>
Alternative implementation, without global assignments:
<syntaxhighlight lang=K>F:{[g] /graph
n:#g /number of vertices
G:@[n#,!0;g;,;!n] /transposed graph
V:{[g;L;x]$[^L?x;(1_(x,L)o[g]/g x),x;L]}[g]
L:|V/[!0;!#g] /Visit
A:{[G;c;u;r]$[0>c u;o[G]/[@[c;u;:;r];G u;r];c]}[G]
.=A/[n#-1;L;L]} /Assign</syntaxhighlight>
(result is the same)
{{works with|Kona}}<syntaxhighlight lang=K>F:{[g] / graph
n: #g / number of vertices
v::&n / visited?
L::!0 / dfs order
V: {[g;x] :[v x;;[v[x]:1;_f[g]'g x;L::x,L]];}[g]
V'!n / Visit
G: @[n#,!0;g;,;!n] / transposed graph
c::n#-1 / assigned components
A: {[G;x;y] :[-1=c x;[c[x]:y;G[x]_f[G]'y];]}[G]'
A'/2#,L / Assign
.=c}</syntaxhighlight>
(result is the same)
=={{header|Kotlin}}==
{{trans|Go}}
<
/* the list index is the first vertex in the edge(s) */
Line 516 ⟶ 873:
fun main(args: Array<String>) {
println(kosaraju(g).joinToString("\n"))
}</
{{out}}
Line 528 ⟶ 885:
=={{header|Lua}}==
{{trans|C++}}
<
io.write("[")
for i=0,#a do
Line 622 ⟶ 979:
write_array(kosaraju(g))
print()</
{{out}}
<pre>[0, 0, 0, 3, 3, 5, 5, 7]</pre>
=={{header|Mathematica}}/{{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">g = Graph[{0 -> 1, 1 -> 2, 2 -> 0, 3 -> 1, 3 -> 2, 3 -> 4, 4 -> 3,
4 -> 5, 5 -> 2, 5 -> 6, 6 -> 5, 7 -> 4, 7 -> 6, 7 -> 7}];
cc = ConnectedComponents[g]
Catenate[ConstantArray[Min[#], Length[#]] & /@ SortBy[cc, First]]</syntaxhighlight>
{{out}}
<pre>{{0, 1, 2}, {5, 6}, {3, 4}, {7}}
{0, 0, 0, 3, 3, 5, 5, 7}</pre>
=={{header|Nim}}==
{{trans|Kotlin}}
<syntaxhighlight lang="nim">type
Vertex = int
Graph = seq[seq[Vertex]]
Scc = seq[Vertex]
func korasaju(g: Graph): seq[Scc] =
var
size = g.len
visited = newSeq[bool](size) # All false by default.
l = newSeq[Vertex](size) # All zero by default.
x = size # Index for filling "l" in reverse order.
t = newSeq[seq[Vertex]](size) # Transposed graph.
c = newSeq[Vertex](size) # Used for component assignment.
func visit(u: Vertex) =
if not visited[u]:
visited[u] = true
for v in g[u]:
visit(v)
t[v].add(u) # Construct transposed graph.
dec x
l[x] = u
func assign(u, root: Vertex) =
if visited[u]:
# Repurpose visited to mean 'unassigned'.
visited[u] = false
c[u] = root
for v in t[u]: v.assign(root)
for u in 0..g.high: u.visit()
for u in l: u.assign(u)
# Build list of strongly connected components.
var prev = -1
for v1, v2 in c:
if v2 != prev:
prev = v2
result.add @[]
result[^1].add v1
when isMainModule:
let g = @[@[1], @[2], @[0], @[1, 2, 4], @[3, 5], @[2, 6], @[5], @[4, 6, 7]]
for scc in korasaju(g): echo $scc</syntaxhighlight>
{{out}}
<pre>@[0, 1, 2]
@[3, 4]
@[5, 6]
@[7]</pre>
=={{header|Pascal}}==
Works with FPC (tested with version 3.2.2).
<syntaxhighlight lang="pascal">
program Kosaraju_SCC;
{$mode objfpc}{$modeswitch arrayoperators}
{$j-}{$coperators on}
type
TDigraph = array of array of Integer;
procedure PrintComponents(const g: TDigraph);
var
Visited: array of Boolean = nil;
RevPostOrder: array of Integer = nil;
gr: TDigraph = nil; //reversed graph
Counter, Next: Integer;
FirstItem: Boolean;
procedure Dfs1(aNode: Integer);
begin
Visited[aNode] := True;
for Next in g[aNode] do begin
gr[Next] += [aNode];
if not Visited[Next] then
Dfs1(Next);
end;
RevPostOrder[Counter] := aNode;
Dec(Counter);
end;
procedure Dfs2(aNode: Integer);
begin
Visited[aNode] := True;
for Next in gr[aNode] do
if not Visited[Next] then
Dfs2(Next);
if FirstItem then begin
FirstItem := False;
Write(aNode);
end else
Write(', ', aNode);
end;
var
Node: Integer;
begin
SetLength(Visited, Length(g));
SetLength(RevPostOrder, Length(g));
SetLength(gr, Length(g));
Counter := High(g);
for Node := 0 to High(g) do
if not Visited[Node] then
Dfs1(Node);
FillChar(Pointer(Visited)^, Length(Visited), 0);
for Node in RevPostOrder do
if not Visited[Node] then begin
FirstItem := True;
Write('[');
Dfs2(Node);
WriteLn(']');
end;
end;
const
g: TDigraph = (
(1),
(2),
(0),
(1, 2, 4),
(3, 5),
(2, 6),
(5),
(4, 6, 7)
);
begin
PrintComponents(g);
end.
</syntaxhighlight>
{{out}}
<pre>
[7]
[4, 3]
[6, 5]
[1, 2, 0]
</pre>
=={{header|Perl}}==
{{trans|
<
use warnings;
use feature 'say';
Line 698 ⟶ 1,203:
kosaraju(%test1);
say '';
kosaraju(%test2);</
{{out}}
<pre>0 1 2
Line 709 ⟶ 1,214:
Fred Gary
Hank</pre>
=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">visited</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">l</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">t</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">c</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">visit</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">g</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">u</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #000000;">visited</span><span style="color: #0000FF;">[</span><span style="color: #000000;">u</span><span style="color: #0000FF;">]</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">visited</span><span style="color: #0000FF;">[</span><span style="color: #000000;">u</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">g</span><span style="color: #0000FF;">[</span><span style="color: #000000;">u</span><span style="color: #0000FF;">])</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">v</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">g</span><span style="color: #0000FF;">[</span><span style="color: #000000;">u</span><span style="color: #0000FF;">][</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">visit</span><span style="color: #0000FF;">(</span><span style="color: #000000;">g</span><span style="color: #0000FF;">,</span><span style="color: #000000;">v</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">t</span><span style="color: #0000FF;">[</span><span style="color: #000000;">v</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">t</span><span style="color: #0000FF;">[</span><span style="color: #000000;">v</span><span style="color: #0000FF;">])</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">u</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000000;">l</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">u</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">assign</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">u</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">root</span><span style="color: #0000FF;">=</span><span style="color: #000000;">u</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">visited</span><span style="color: #0000FF;">[</span><span style="color: #000000;">u</span><span style="color: #0000FF;">]</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">visited</span><span style="color: #0000FF;">[</span><span style="color: #000000;">u</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span>
<span style="color: #000000;">c</span><span style="color: #0000FF;">[</span><span style="color: #000000;">u</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">root</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">v</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">t</span><span style="color: #0000FF;">[</span><span style="color: #000000;">u</span><span style="color: #0000FF;">])</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">assign</span><span style="color: #0000FF;">(</span><span style="color: #000000;">t</span><span style="color: #0000FF;">[</span><span style="color: #000000;">u</span><span style="color: #0000FF;">][</span><span style="color: #000000;">v</span><span style="color: #0000FF;">],</span> <span style="color: #000000;">root</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">korasaju</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">g</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">len</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">g</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">visited</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #004600;">false</span><span style="color: #0000FF;">,</span><span style="color: #000000;">len</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">l</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #000000;">t</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">({},</span><span style="color: #000000;">len</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">u</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">len</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">visit</span><span style="color: #0000FF;">(</span><span style="color: #000000;">g</span><span style="color: #0000FF;">,</span><span style="color: #000000;">u</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">len</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">u</span><span style="color: #0000FF;">=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">l</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">to</span> <span style="color: #000000;">1</span> <span style="color: #008080;">by</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">assign</span><span style="color: #0000FF;">(</span><span style="color: #000000;">l</span><span style="color: #0000FF;">[</span><span style="color: #000000;">u</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">c</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">g</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">3</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">3</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">5</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">6</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">7</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">6</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">5</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">7</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">8</span><span style="color: #0000FF;">}}</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">korasaju</span><span style="color: #0000FF;">(</span><span style="color: #000000;">g</span><span style="color: #0000FF;">)</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
Line 821 ⟶ 1,267:
=={{header|Python}}==
Works with Python 2
<syntaxhighlight lang="python">def kosaraju(g):
class nonlocal: pass
Line 860 ⟶ 1,307:
g = [[1], [2], [0], [1,2,4], [3,5], [2,6], [5], [4,6,7]]
print kosaraju(g)</
{{out}}
<pre>[0, 0, 0, 3, 3, 5, 5, 7]</pre>
Syntax requirements have changed. This version works with Python 3.
<syntaxhighlight lang="python3">def kosaraju(g):
size = len(g)
vis = [False] * size
l = [0] * size
x = size
t = [[] for _ in range(size)]
def visit(u):
nonlocal x
if not vis[u]:
vis[u] = True
for v in g[u]:
visit(v)
t[v].append(u)
x -= 1
l[x] = u
for u in range(size):
visit(u)
c = [0] * size
def assign(u, root):
if vis[u]:
vis[u] = False
c[u] = root
for v in t[u]:
assign(v, root)
for u in l:
assign(u, u)
return c
g = [[1], [2], [0], [1, 2, 4], [3, 5], [2, 6], [5], [4, 6, 7]]
print(kosaraju(g))</syntaxhighlight>
{{out}}
<pre>[0, 0, 0, 3, 3, 5, 5, 7]</pre>
=={{header|Racket}}==
<
(require racket/dict)
Line 897 ⟶ 1,386:
(3 1 2 4) ; equvalent to (3 . (1 2 4))
(4 5 3)
(7 4 7 6))))</
{{out}}
<pre>'((7) (6 5) (4 3) (2 1 0))</pre>
=={{header|Raku}}==
(formerly Perl 6)
{{works with|Rakudo|2018.09}}
Inspired by Python & Kotlin entries.
Accepts a hash of lists/arrays holding the vertex (name => (neighbors)) pairs. No longer limited to continuous, positive, integer vertex names.
<syntaxhighlight lang="raku" line>sub kosaraju (%k) {
my %g = %k.keys.sort Z=> flat ^%k;
my %h = %g.invert;
my %visited;
my @stack;
my @transpose;
my @connected;
sub visit ($u) {
unless %visited{$u} {
%visited{$u} = True;
for |%k{$u} -> $v {
visit($v);
@transpose[%g{$v}].push: $u;
}
@stack.push: $u;
}
}
sub assign ($u, $root) {
if %visited{$u} {
%visited{$u} = False;
@connected[%g{$u}] = $root;
assign($_, $root) for |@transpose[%g{$u}];
}
}
.&visit for %g.keys;
assign($_, $_) for @stack.reverse;
(|%g{@connected}).pairs.categorize( *.value, :as(*.key) ).values.map: { %h{|$_} };
}
# TESTING
-> $test { say "\nStrongly connected components: ", |kosaraju($test).sort } for
# Same test data as all other entries, converted to a hash of lists
(((1),(2),(0),(1,2,4),(3,5),(2,6),(5),(4,6,7)).pairs.hash),
# Same layout test data with named vertices instead of numbered.
(
%(:Andy<Bart>,
:Bart<Carl>,
:Carl<Andy>,
:Dave<Bart Carl Earl>,
:Earl<Dave Fred>,
:Fred<Carl Gary>,
:Gary<Fred>,
:Hank<Earl Gary Hank>)
)</syntaxhighlight>
{{out}}
<pre>
Strongly connected components: (0 1 2)(3 4)(5 6)(7)
Strongly connected components: (Andy Bart Carl)(Dave Earl)(Fred Gary)(Hank)</pre>
=={{header|Sidef}}==
{{trans|Julia}}
<
# 1. For each vertex u of the graph, mark u as unvisited. Let L be empty.
var vis = g.len.of(false)
Line 951 ⟶ 1,504:
var g = [[1], [2], [0], [1, 2, 4], [3, 5], [2, 6], [5], [4, 6, 7]]
say korasaju(g)</
{{out}}
<pre>
[0, 0, 0, 3, 3, 5, 5, 7]
</pre>
=={{header|Standard ML}}==
<syntaxhighlight lang="sml">datatype 'a node = Node of 'a * bool ref * 'a node list ref * 'a node list ref
fun node x = Node (x, ref false, ref nil, ref nil)
fun mark (Node (_, r, _, _)) = !r before r := true
fun unmark (Node (_, r, _, _)) = !r before r := false
fun value (Node (x, _, _, _)) = x
fun sources (Node (_, _, ref xs, _)) = xs
fun targets (Node (_, _, _, ref ys)) = ys
fun connect (m, n) =
let
val Node (_, _, _, ns) = m
val Node (_, _, ms, _) = n
in
ms := m :: !ms;
ns := n :: !ns
end
datatype 'a step = One of 'a | Many of 'a list
fun visit (ms, nil) = ms
| visit (ms, One m :: ss) = visit (m :: ms, ss)
| visit (ms, Many nil :: ss) = visit (ms, ss)
| visit (ms, Many (n :: ns) :: ss) =
if mark n then
visit (ms, Many ns :: ss)
else
visit (ms, Many (targets n) :: One n :: Many ns :: ss)
fun assign (xs, nil) = xs
| assign (xs, nil :: ss) = assign (xs, ss)
| assign (xs, (n :: ns) :: ss) =
if unmark n then
assign (value n :: xs, sources n :: ns :: ss)
else
assign (xs, ns :: ss)
fun assigns (xs, nil) = xs
| assigns (xs, n :: ns) =
if unmark n then
let
val x = sources n :: nil
val x = value n :: assign (nil, x)
in
assigns (x :: xs, ns)
end
else
assigns (xs, ns)
fun kosaraju xs = assigns (nil, visit (nil, Many xs :: nil))
fun make (n, is, ijs) =
let
val xs = Vector.tabulate (n, node)
fun item i = Vector.sub (xs, i)
fun step (i, j) = connect (item i, item j)
fun path (i :: j :: js) = (step (i, j); path (j :: js))
| path _ = ()
in
map item is before app path ijs
end
val is = 0 :: nil
val ijs =
[0, 1, 2, 0, 3, 4, 0, 5, 7] ::
[0, 9, 10, 11, 12, 9, 11] ::
[1, 12] ::
[3, 5, 6, 7, 8, 6, 15] ::
[5, 13, 14, 13, 15] ::
[8, 15] ::
[10, 13] ::
nil
val ns = make (16, is, ijs)
val xs = kosaraju ns</syntaxhighlight>
{{out}}
<pre>
> xs;
val it = [[15], [13, 14], [9, 10, 11, 12], [6, 7, 8], [5], [0, 1, 2, 3, 4]]: int list list
</pre>
=={{header|Swift}}==
{{trans|D}}
<syntaxhighlight lang="swift">func kosaraju(graph: [[Int]]) -> [Int] {
let size = graph.count
var x = size
var vis = [Bool](repeating: false, count: size)
var l = [Int](repeating: 0, count: size)
var c = [Int](repeating: 0, count: size)
var t = [[Int]](repeating: [], count: size)
func visit(_ u: Int) {
guard !vis[u] else {
return
}
vis[u] = true
for v in graph[u] {
visit(v)
t[v].append(u)
}
x -= 1
l[x] = u
}
for u in 0..<graph.count {
visit(u)
}
func assign(_ u: Int, root: Int) {
guard vis[u] else {
return
}
vis[u] = false
c[u] = root
for v in t[u] {
assign(v, root: root)
}
}
for u in l {
assign(u, root: u)
}
return c
}
let graph = [
[1],
[2],
[0],
[1, 2, 4],
[3, 5],
[2, 6],
[5],
[4, 6, 7]
]
print(kosaraju(graph: graph))</syntaxhighlight>
{{out}}
<pre>[0, 0, 0, 3, 3, 5, 5, 7]</pre>
=={{header|Visual Basic .NET}}==
{{trans|Java}}
<syntaxhighlight lang="vbnet">Module Module1
Function Kosaraju(g As List(Of List(Of Integer))) As List(Of Integer)
Dim size = g.Count
Dim vis(size - 1) As Boolean
Dim l(size - 1) As Integer
Dim x = size
Dim t As New List(Of List(Of Integer))
For i = 1 To size
t.Add(New List(Of Integer))
Next
Dim visit As Action(Of Integer) = Sub(u As Integer)
If Not vis(u) Then
vis(u) = True
For Each v In g(u)
visit(v)
t(v).Add(u)
Next
x -= 1
l(x) = u
End If
End Sub
For i = 1 To size
visit(i - 1)
Next
Dim c(size - 1) As Integer
Dim assign As Action(Of Integer, Integer) = Sub(u As Integer, root As Integer)
If vis(u) Then
vis(u) = False
c(u) = root
For Each v In t(u)
assign(v, root)
Next
End If
End Sub
For Each u In l
assign(u, u)
Next
Return c.ToList
End Function
Sub Main()
Dim g = New List(Of List(Of Integer)) From {
New List(Of Integer) From {1},
New List(Of Integer) From {2},
New List(Of Integer) From {0},
New List(Of Integer) From {1, 2, 4},
New List(Of Integer) From {3, 5},
New List(Of Integer) From {2, 6},
New List(Of Integer) From {5},
New List(Of Integer) From {4, 6, 7}
}
Dim output = Kosaraju(g)
Console.WriteLine("[{0}]", String.Join(", ", output))
End Sub
End Module</syntaxhighlight>
{{out}}
<pre>[0, 0, 0, 3, 3, 5, 5, 7]</pre>
=={{header|Wren}}==
{{trans|Go}}
<syntaxhighlight lang="wren">var kosaraju = Fn.new { |g|
var gc = g.count
// 1. For each vertex u of the graph, mark u as unvisited. Let l be empty.
var vis = List.filled(gc, false)
var l = List.filled(gc, 0)
var x = gc // index for filling l in reverse order
var t = List.filled(gc, null) // transpose graph
for (i in 0...gc) t[i] = []
var visit // recursive function
visit = Fn.new { |u|
if (!vis[u]) {
vis[u] = true
for (v in g[u]) {
visit.call(v)
t[v].add(u) // construct transpose
}
x = x - 1
l[x] = u
}
}
// 2. For each vertex u of the graph do visit.call(u).
for (i in 0...gc) visit.call(i)
var c = List.filled(gc, 0) // result, the component assignment
var assign // recursive function
assign = Fn.new { |u, root|
if (vis[u]) { // repurpose vis to mean 'unassigned'
vis[u] = false
c[u] = root
for (v in t[u]) assign.call(v, root)
}
}
// 3: For each element u of l in order, do assign.call(u,u).
for (u in l) assign.call(u, u)
return c
}
var g = [ [1], [2], [0], [1, 2, 4], [3, 5], [2, 6], [5], [4, 6, 7] ]
System.print(kosaraju.call(g))</syntaxhighlight>
{{out}}
<pre>
Line 958 ⟶ 1,778:
=={{header|zkl}}==
<
fcn visit(u,G,L){ // u is ((visited,assigned), (id,edges))
Line 998 ⟶ 1,818:
return(components);
}</
<
// with vertices id zero based (vs 1 based in article)
// ids start at zero and are consecutive (no holes), graph is unsorted
Line 1,005 ⟶ 1,825:
T( T(0,1), T(2,0), T(5,2,6), T(6,5),
T(1,2), T(3,1,2,4), T(4,5,3), T(7,4,7,6) );
kosaraju(graph);</
{{out}}
<pre>
|
Latest revision as of 08:33, 7 May 2024
You are encouraged to solve this task according to the task description, using any language you may know.
This page uses content from Wikipedia. The original article was at Graph. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance) |
Kosaraju's algorithm (also known as the Kosaraju–Sharir algorithm) is a linear time algorithm to find the strongly connected components of a directed graph. Aho, Hopcroft and Ullman credit it to an unpublished paper from 1978 by S. Rao Kosaraju. The same algorithm was independently discovered by Micha Sharir and published by him in 1981. It makes use of the fact that the transpose graph (the same graph with the direction of every edge reversed) has exactly the same strongly connected components as the original graph.
For this task consider the directed graph with these connections:
0 -> 1 1 -> 2 2 -> 0 3 -> 1, 3 -> 2, 3 -> 4 4 -> 3, 4 -> 5 5 -> 2, 5 -> 6 6 -> 5 7 -> 4, 7 -> 6, 7 -> 7
And report the kosaraju strongly connected component for each node.
- References
- The article on Wikipedia.
11l
F kosaraju(g)
V size = g.len
V vis = [0B] * size
V l = [0] * size
V x = size
V t = [[Int]()] * size
F visit(Int u) -> Void
I !@vis[u]
@vis[u] = 1B
L(v) @g[u]
@visit(v)
@t[v] [+]= u
@x--
@l[@x] = u
L(u) 0 .< g.len
visit(u)
V c = [0] * size
F assign(Int u, root) -> Void
I @vis[u]
@vis[u] = 0B
@c[u] = root
L(v) @t[u]
@assign(v, root)
L(u) l
assign(u, u)
R c
V g = [[1], [2], [0], [1, 2, 4], [3, 5], [2, 6], [5], [4, 6, 7]]
print(kosaraju(g))
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
C#
using System;
using System.Collections.Generic;
class Node
{
public enum Colors
{
Black, White, Gray
}
public Colors color { get; set; }
public int N { get; }
public Node(int n)
{
N = n;
color = Colors.White;
}
}
class Graph
{
public HashSet<Node> V { get; }
public Dictionary<Node, HashSet<Node>> Adj { get; }
/// <summary>
/// Kosaraju's strongly connected components algorithm
/// </summary>
public void Kosaraju()
{
var L = new HashSet<Node>();
Action<Node> Visit = null;
Visit = (u) =>
{
if (u.color == Node.Colors.White)
{
u.color = Node.Colors.Gray;
foreach (var v in Adj[u])
Visit(v);
L.Add(u);
}
};
Action<Node, Node> Assign = null;
Assign = (u, root) =>
{
if (u.color != Node.Colors.Black)
{
if (u == root)
Console.Write("SCC: ");
Console.Write(u.N + " ");
u.color = Node.Colors.Black;
foreach (var v in Adj[u])
Assign(v, root);
if (u == root)
Console.WriteLine();
}
};
foreach (var u in V)
Visit(u);
foreach (var u in L)
Assign(u, u);
}
}
C++
#include <functional>
#include <iostream>
#include <ostream>
#include <vector>
template<typename T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
auto it = v.cbegin();
auto end = v.cend();
os << "[";
if (it != end) {
os << *it;
it = std::next(it);
}
while (it != end) {
os << ", " << *it;
it = std::next(it);
}
return os << "]";
}
std::vector<int> kosaraju(std::vector<std::vector<int>>& g) {
// 1. For each vertex u of the graph, mark u as unvisited. Let l be empty.
auto size = g.size();
std::vector<bool> vis(size); // all false by default
std::vector<int> l(size); // all zero by default
auto x = size; // index for filling l in reverse order
std::vector<std::vector<int>> t(size); // transpose graph
// Recursive subroutine 'visit':
std::function<void(int)> visit;
visit = [&](int u) {
if (!vis[u]) {
vis[u] = true;
for (auto v : g[u]) {
visit(v);
t[v].push_back(u); // construct transpose
}
l[--x] = u;
}
};
// 2. For each vertex u of the graph do visit(u)
for (int i = 0; i < g.size(); ++i) {
visit(i);
}
std::vector<int> c(size); // used for component assignment
// Recursive subroutine 'assign':
std::function<void(int, int)> assign;
assign = [&](int u, int root) {
if (vis[u]) { // repurpose vis to mean 'unassigned'
vis[u] = false;
c[u] = root;
for (auto v : t[u]) {
assign(v, root);
}
}
};
// 3: For each element u of l in order, do assign(u, u)
for (auto u : l) {
assign(u, u);
}
return c;
}
std::vector<std::vector<int>> g = {
{1},
{2},
{0},
{1, 2, 4},
{3, 5},
{2, 6},
{5},
{4, 6, 7},
};
int main() {
using namespace std;
cout << kosaraju(g) << endl;
return 0;
}
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
D
(mostly) with output like
import std.container.array;
import std.stdio;
/* the list index is the first vertex in the edge(s) */
auto g = [
[1],
[2],
[0],
[1, 2, 4],
[3, 5],
[2, 6],
[5],
[4, 6, 7],
];
int[] kosaraju(int[][] g) {
// 1. For each vertex u of the graph, mark u as unvisited. Let l be empty.
auto size = g.length; // all false by default
Array!bool vis;
vis.length = size;
int[] l; // all zero by default
l.length = size;
auto x = size; // index for filling l in reverse order
int[][] t; // transpose graph
t.length = size;
// Recursive subroutine 'visit':
void visit(int u) {
if (!vis[u]) {
vis[u] = true;
foreach (v; g[u]) {
visit(v);
t[v] ~= u; // construct transpose
}
l[--x] = u;
}
}
// 2. For each vertex u of the graph do visit(u)
foreach (u, _; g) {
visit(u);
}
int[] c; // used for component assignment
c.length = size;
// Recursive subroutine 'assign':
void assign(int u, int root) {
if (vis[u]) { // repurpose vis to mean 'unassigned'
vis[u] = false;
c[u] = root;
foreach(v; t[u]) {
assign(v, root);
}
}
}
// 3: For each element u of l in order, do assign(u, u)
foreach (u; l) {
assign(u, u);
}
return c;
}
void main() {
writeln(kosaraju(g));
}
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
Delphi
program KosarajuApp;
uses
System.SysUtils;
type
TKosaraju = TArray<TArray<Integer>>;
var
g: TKosaraju;
procedure Init();
begin
SetLength(g, 8);
g[0] := [1];
g[1] := [2];
g[2] := [0];
g[3] := [1, 2, 4];
g[4] := [3, 5];
g[5] := [2, 6];
g[6] := [5];
g[7] := [4, 6, 7];
end;
procedure Println(vector: TArray<Integer>);
var
i: Integer;
begin
write('[');
if (Length(vector) > 0) then
for i := 0 to High(vector) do
begin
write(vector[i]);
if (i < high(vector)) then
write(', ');
end;
writeln(']');
end;
function Kosaraju(g: TKosaraju): TArray<Integer>;
var
vis: TArray<Boolean>;
L, c: TArray<Integer>;
x: Integer;
t: TArray<TArray<Integer>>;
Visit: TProc<Integer>;
u: Integer;
Assign: TProc<Integer, Integer>;
begin
// 1. For each vertex u of the graph, mark u as unvisited. Let L be empty.
SetLength(vis, Length(g));
SetLength(L, Length(g));
x := Length(L);
// index for filling L in reverse order
SetLength(t, Length(g)); // transpose graph
// 2. recursive subroutine:
Visit := procedure(u: Integer)
begin
if (not vis[u]) then
begin
vis[u] := true;
for var v in g[u] do
begin
Visit(v);
t[v] := concat(t[v], [u]);
// construct transpose
end;
dec(x);
L[x] := u;
end;
end;
// 2. For each vertex u of the graph do Visit(u)
for u := 0 to High(g) do
begin
Visit(u);
end;
SetLength(c, Length(g)); // result, the component assignment
// 3: recursive subroutine:
Assign := procedure(u, root: Integer)
begin
if vis[u] then
// repurpose vis to mean "unassigned"
begin
vis[u] := false;
c[u] := root;
for var v in t[u] do
Assign(v, root);
end;
end;
// 3: For each element u of L in order, do Assign(u,u)
for u in L do
Assign(u, u);
Result := c;
end;
begin
Init;
Println(Kosaraju(g));
end.
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
Go
package main
import "fmt"
var g = [][]int{
0: {1},
1: {2},
2: {0},
3: {1, 2, 4},
4: {3, 5},
5: {2, 6},
6: {5},
7: {4, 6, 7},
}
func main() {
fmt.Println(kosaraju(g))
}
func kosaraju(g [][]int) []int {
// 1. For each vertex u of the graph, mark u as unvisited. Let L be empty.
vis := make([]bool, len(g))
L := make([]int, len(g))
x := len(L) // index for filling L in reverse order
t := make([][]int, len(g)) // transpose graph
// 2. recursive subroutine:
var Visit func(int)
Visit = func(u int) {
if !vis[u] {
vis[u] = true
for _, v := range g[u] {
Visit(v)
t[v] = append(t[v], u) // construct transpose
}
x--
L[x] = u
}
}
// 2. For each vertex u of the graph do Visit(u)
for u := range g {
Visit(u)
}
c := make([]int, len(g)) // result, the component assignment
// 3: recursive subroutine:
var Assign func(int, int)
Assign = func(u, root int) {
if vis[u] { // repurpose vis to mean "unassigned"
vis[u] = false
c[u] = root
for _, v := range t[u] {
Assign(v, root)
}
}
}
// 3: For each element u of L in order, do Assign(u,u)
for _, u := range L {
Assign(u, u)
}
return c
}
- Output:
[0 0 0 3 3 5 5 7]
J
Implementation:
kosaraju=: {{
coerase([cocurrent)cocreate''
visit=: {{
if.y{unvisited do.
unvisited=: 0 y} unvisited
visit y{::out
L=: y,L
end.
}}"0
assign=: {{
if._1=y{assigned do.
assigned=: x y} assigned
x&assign y{::in
end.
}}"0
out=: y
in=: <@I.|:y e.S:0~i.#y
unvisited=: 1#~#y
assigned=: _1#~#y
L=: i.0
visit"0 i.#y
assign~L
assigned
}}
Task example (tree representation: each value here is the index of the parent node):
kosaraju 1;2;0;1 2 4;3 5;2 6;5;4 6 7
0 0 0 3 3 5 5 7
Alternatively, we could represent the result as a graph of the same type as our argument graph. The implementation of visit remains the same:
kosarajud=: {{
coerase([cocurrent)cocreate''
visit=: {{
if.y{unvisited do.
unvisited=: 0 y} unvisited
visit y{::out
L=: y,L
end.
}}"0
assign=: {{
if.-.y e.;assigned do.
assigned=: (y,L:0~x{assigned) x} assigned
x&assign y{::in
end.
}}"0
out=: y
in=: <@I.|:y e.S:0~i.#y
unvisited=: 1#~#y
assigned=: a:#~#y
L=: i.0
visit"0 i.#y
assign~L
assigned
}}
kosarajud 1;2;0;1 2 4;3 5;2 6;5;4 6 7
┌─────┬┬┬───┬┬───┬┬─┐
│0 2 1│││3 4││5 6││7│
└─────┴┴┴───┴┴───┴┴─┘
But it would probably be simpler to convert the result from the tree representation to our directed graph representation:
((<@I.@e."1 0)i.@#) 0 0 0 3 3 5 5 7
┌─────┬┬┬───┬┬───┬┬─┐
│0 1 2│││3 4││5 6││7│
└─────┴┴┴───┴┴───┴┴─┘
Java
Output is like Go instead of what Kotlin outputs.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.IntConsumer;
import java.util.stream.Collectors;
public class Kosaraju {
static class Recursive<I> {
I func;
}
private static List<Integer> kosaraju(List<List<Integer>> g) {
// 1. For each vertex u of the graph, mark u as unvisited. Let l be empty.
int size = g.size();
boolean[] vis = new boolean[size];
int[] l = new int[size];
AtomicInteger x = new AtomicInteger(size);
List<List<Integer>> t = new ArrayList<>();
for (int i = 0; i < size; ++i) {
t.add(new ArrayList<>());
}
Recursive<IntConsumer> visit = new Recursive<>();
visit.func = (int u) -> {
if (!vis[u]) {
vis[u] = true;
for (Integer v : g.get(u)) {
visit.func.accept(v);
t.get(v).add(u);
}
int xval = x.decrementAndGet();
l[xval] = u;
}
};
// 2. For each vertex u of the graph do visit(u)
for (int i = 0; i < size; ++i) {
visit.func.accept(i);
}
int[] c = new int[size];
Recursive<BiConsumer<Integer, Integer>> assign = new Recursive<>();
assign.func = (Integer u, Integer root) -> {
if (vis[u]) { // repurpose vis to mean 'unassigned'
vis[u] = false;
c[u] = root;
for (Integer v : t.get(u)) {
assign.func.accept(v, root);
}
}
};
// 3: For each element u of l in order, do assign(u, u)
for (int u : l) {
assign.func.accept(u, u);
}
return Arrays.stream(c).boxed().collect(Collectors.toList());
}
public static void main(String[] args) {
List<List<Integer>> g = new ArrayList<>();
for (int i = 0; i < 8; ++i) {
g.add(new ArrayList<>());
}
g.get(0).add(1);
g.get(1).add(2);
g.get(2).add(0);
g.get(3).add(1);
g.get(3).add(2);
g.get(3).add(4);
g.get(4).add(3);
g.get(4).add(5);
g.get(5).add(2);
g.get(5).add(6);
g.get(6).add(5);
g.get(7).add(4);
g.get(7).add(6);
g.get(7).add(7);
List<Integer> output = kosaraju(g);
System.out.println(output);
}
}
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
jq
# Fill an array of the specified length with the input value
def dimension($n): . as $in | [range(0;$n) | $in];
# $graph should be an adjacency-list graph with IO==0
def korasaju($graph):
($graph|length) as $length
| def init: {
vis: (false | dimension($length)), # visited
L: [], # for an array of $length integers
t: ([]|dimension($length)), # transposed graph
x: $length # index
};
# input: {vis, L, t, x, t}
def visit($u):
if .vis[$u] | not
then .vis[$u] = true
| reduce ($graph[$u][]) as $v (.;
visit($v)
| .t[$v] += [$u] )
| .x -= 1
| .L[.x] = $u
else .
end ;
# input: {vis, t, c}
def assign($u; $root):
if .vis[$u]
then .vis[$u] = false
| .c[$u] = $root
| reduce .t[$u][] as $v (.; assign($v; $root))
else .
end ;
# For each vertex u of the graph, mark u as unvisited.
init
# For each vertex u of the graph do visit(u)
| reduce range(0;$length) as $u (.; visit($u))
| .c = (null|dimension($length))
# For each element u of L in order, do assign(u, u)
| reduce .L[] as $u (.; assign($u; $u) )
| .c ;
# An example adjacency list using IO==1
def g: [
[1],
[2],
[0],
[1, 2, 4],
[3, 5],
[2, 6],
[5],
[4, 6, 7]
];
korasaju(g)
- Output:
With the jq program above in korasaju.jq, the invocation `jq -nc -f korasaju.jq` produces:
[0,0,0,3,3,5,5,7]
Julia
function korasaju(g::Vector{Vector{T}}) where T<:Integer
# 1. For each vertex u of the graph, mark u as unvisited. Let L be empty.
vis = falses(length(g))
L = Vector{T}(length(g))
x = length(L) + 1
t = collect(T[] for _ in eachindex(g))
# Recursive
function visit(u::T)
if !vis[u]
vis[u] = true
for v in g[u]
visit(v)
push!(t[v], u)
end
x -= 1
L[x] = u
end
end
# 2. For each vertex u of the graph do visit(u)
for u in eachindex(g)
visit(u)
end
c = Vector{T}(length(g))
# 3. Recursive subroutine:
function assign(u::T, root::T)
if vis[u]
vis[u] = false
c[u] = root
for v in t[u]
assign(v, root)
end
end
end
# 3. For each element u of L in order, do assign(u, u)
for u in L
assign(u, u)
end
return c
end
g = [[2], [3], [1], [2, 3, 5], [4, 6], [3, 7], [6], [5, 7, 8]]
println(korasaju(g))
- Output:
[1, 1, 1, 4, 4, 6, 6, 8]
K
F:{[g] / graph
n: #g / number of vertices
v::&n / visited?
L::!0 / dfs order
V: {[g;x] $[v x;;[v[x]:1;o[g]'g x;L::x,L]];}[g]
V'!n / Visit
G: @[n#,!0;g;,;!n] / transposed graph
c::n#-1 / assigned components
A: {[G;x;y] $[-1=c x;[c[x]:y;G[x]o[G]'y];]}[G]'
A'/2#,L / Assign
.=c}
F(1;2;0;1 2 4;3 5;2 6;5;4 6 7)
(0 1 2
3 4
5 6
,7)
Alternative implementation, without global assignments:
F:{[g] /graph
n:#g /number of vertices
G:@[n#,!0;g;,;!n] /transposed graph
V:{[g;L;x]$[^L?x;(1_(x,L)o[g]/g x),x;L]}[g]
L:|V/[!0;!#g] /Visit
A:{[G;c;u;r]$[0>c u;o[G]/[@[c;u;:;r];G u;r];c]}[G]
.=A/[n#-1;L;L]} /Assign
(result is the same)
F:{[g] / graph
n: #g / number of vertices
v::&n / visited?
L::!0 / dfs order
V: {[g;x] :[v x;;[v[x]:1;_f[g]'g x;L::x,L]];}[g]
V'!n / Visit
G: @[n#,!0;g;,;!n] / transposed graph
c::n#-1 / assigned components
A: {[G;x;y] :[-1=c x;[c[x]:y;G[x]_f[G]'y];]}[G]'
A'/2#,L / Assign
.=c}
(result is the same)
Kotlin
// version 1.1.3
/* the list index is the first vertex in the edge(s) */
val g = listOf(
intArrayOf(1), // 0
intArrayOf(2), // 1
intArrayOf(0), // 2
intArrayOf(1, 2, 4), // 3
intArrayOf(3, 5), // 4
intArrayOf(2, 6), // 5
intArrayOf(5), // 6
intArrayOf(4, 6, 7) // 7
)
fun kosaraju(g: List<IntArray>): List<List<Int>> {
// 1. For each vertex u of the graph, mark u as unvisited. Let l be empty.
val size = g.size
val vis = BooleanArray(size) // all false by default
val l = IntArray(size) // all zero by default
var x = size // index for filling l in reverse order
val t = List(size) { mutableListOf<Int>() } // transpose graph
// Recursive subroutine 'visit':
fun visit(u: Int) {
if (!vis[u]) {
vis[u] = true
for (v in g[u]) {
visit(v)
t[v].add(u) // construct transpose
}
l[--x] = u
}
}
// 2. For each vertex u of the graph do visit(u)
for (u in g.indices) visit(u)
val c = IntArray(size) // used for component assignment
// Recursive subroutine 'assign':
fun assign(u: Int, root: Int) {
if (vis[u]) { // repurpose vis to mean 'unassigned'
vis[u] = false
c[u] = root
for (v in t[u]) assign(v, root)
}
}
// 3: For each element u of l in order, do assign(u, u)
for (u in l) assign(u, u)
// Obtain list of SCC's from 'c' and return it
return c.withIndex()
.groupBy { it.value }.values
.map { ivl -> ivl.map { it.index } }
}
fun main(args: Array<String>) {
println(kosaraju(g).joinToString("\n"))
}
- Output:
[0, 1, 2] [3, 4] [5, 6] [7]
Lua
function write_array(a)
io.write("[")
for i=0,#a do
if i>0 then
io.write(", ")
end
io.write(tostring(a[i]))
end
io.write("]")
end
function kosaraju(g)
-- 1. For each vertex u of the graph, mark u as unvisited. Let l be empty.
local size = #g
local vis = {}
for i=0,size do
-- all false by default
vis[i] = false
end
local l = {}
for i=0,size do
-- all zero by default
l[i] = 0
end
local x = size+1 -- index for filling l in reverse order
local t = {} -- transpose graph
-- Recursive subroutine 'visit'
function visit(u)
if not vis[u] then
vis[u] = true
for i=0,#g[u] do
local v = g[u][i]
visit(v)
if t[v] then
local a = t[v]
a[#a+1] = u
else
t[v] = {[0]=u}
end
end
x = x - 1
l[x] = u
end
end
-- 2. For each vertex u of the graph do visit(u)
for i=0,#g do
visit(i)
end
local c = {}
for i=0,size do
-- used for component assignment
c[i] = 0
end
-- Recursive subroutine 'assign'
function assign(u, root)
if vis[u] then -- repurpose vis to mean 'unassigned'
vis[u] = false
c[u] = root
for i=0,#t[u] do
local v = t[u][i]
assign(v, root)
end
end
end
-- 3: For each element u of l in order, do assign(u, u)
for i=0,#l do
local u = l[i]
assign(u, u)
end
return c
end
-- main
local g = {
[0]={[0]=1},
[1]={[0]=2},
[2]={[0]=0},
[3]={[0]=1, [1]=2, [2]=4},
[4]={[0]=3, [1]=5},
[5]={[0]=2, [1]=6},
[6]={[0]=5},
[7]={[0]=4, [1]=6, [2]=7},
}
write_array(kosaraju(g))
print()
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
Mathematica/Wolfram Language
g = Graph[{0 -> 1, 1 -> 2, 2 -> 0, 3 -> 1, 3 -> 2, 3 -> 4, 4 -> 3,
4 -> 5, 5 -> 2, 5 -> 6, 6 -> 5, 7 -> 4, 7 -> 6, 7 -> 7}];
cc = ConnectedComponents[g]
Catenate[ConstantArray[Min[#], Length[#]] & /@ SortBy[cc, First]]
- Output:
{{0, 1, 2}, {5, 6}, {3, 4}, {7}} {0, 0, 0, 3, 3, 5, 5, 7}
Nim
type
Vertex = int
Graph = seq[seq[Vertex]]
Scc = seq[Vertex]
func korasaju(g: Graph): seq[Scc] =
var
size = g.len
visited = newSeq[bool](size) # All false by default.
l = newSeq[Vertex](size) # All zero by default.
x = size # Index for filling "l" in reverse order.
t = newSeq[seq[Vertex]](size) # Transposed graph.
c = newSeq[Vertex](size) # Used for component assignment.
func visit(u: Vertex) =
if not visited[u]:
visited[u] = true
for v in g[u]:
visit(v)
t[v].add(u) # Construct transposed graph.
dec x
l[x] = u
func assign(u, root: Vertex) =
if visited[u]:
# Repurpose visited to mean 'unassigned'.
visited[u] = false
c[u] = root
for v in t[u]: v.assign(root)
for u in 0..g.high: u.visit()
for u in l: u.assign(u)
# Build list of strongly connected components.
var prev = -1
for v1, v2 in c:
if v2 != prev:
prev = v2
result.add @[]
result[^1].add v1
when isMainModule:
let g = @[@[1], @[2], @[0], @[1, 2, 4], @[3, 5], @[2, 6], @[5], @[4, 6, 7]]
for scc in korasaju(g): echo $scc
- Output:
@[0, 1, 2] @[3, 4] @[5, 6] @[7]
Pascal
Works with FPC (tested with version 3.2.2).
program Kosaraju_SCC;
{$mode objfpc}{$modeswitch arrayoperators}
{$j-}{$coperators on}
type
TDigraph = array of array of Integer;
procedure PrintComponents(const g: TDigraph);
var
Visited: array of Boolean = nil;
RevPostOrder: array of Integer = nil;
gr: TDigraph = nil; //reversed graph
Counter, Next: Integer;
FirstItem: Boolean;
procedure Dfs1(aNode: Integer);
begin
Visited[aNode] := True;
for Next in g[aNode] do begin
gr[Next] += [aNode];
if not Visited[Next] then
Dfs1(Next);
end;
RevPostOrder[Counter] := aNode;
Dec(Counter);
end;
procedure Dfs2(aNode: Integer);
begin
Visited[aNode] := True;
for Next in gr[aNode] do
if not Visited[Next] then
Dfs2(Next);
if FirstItem then begin
FirstItem := False;
Write(aNode);
end else
Write(', ', aNode);
end;
var
Node: Integer;
begin
SetLength(Visited, Length(g));
SetLength(RevPostOrder, Length(g));
SetLength(gr, Length(g));
Counter := High(g);
for Node := 0 to High(g) do
if not Visited[Node] then
Dfs1(Node);
FillChar(Pointer(Visited)^, Length(Visited), 0);
for Node in RevPostOrder do
if not Visited[Node] then begin
FirstItem := True;
Write('[');
Dfs2(Node);
WriteLn(']');
end;
end;
const
g: TDigraph = (
(1),
(2),
(0),
(1, 2, 4),
(3, 5),
(2, 6),
(5),
(4, 6, 7)
);
begin
PrintComponents(g);
end.
- Output:
[7] [4, 3] [6, 5] [1, 2, 0]
Perl
use strict;
use warnings;
use feature 'say';
sub kosaraju {
our(%k) = @_;
our %g = ();
our %h;
my $i = 0;
$g{$_} = $i++ for sort keys %k;
$h{$g{$_}} = $_ for keys %g; # invert
our(%visited, @stack, @transpose, @connected);
sub visit {
my($u) = @_;
unless ($visited{$u}) {
$visited{$u} = 1;
for my $v (@{$k{$u}}) {
visit($v);
push @{$transpose[$g{$v}]}, $u;
}
push @stack, $u;
}
}
sub assign {
my($u, $root) = @_;
if ($visited{$u}) {
$visited{$u} = 0;
$connected[$g{$u}] = $root;
assign($_, $root) for @{$transpose[$g{$u}]};
}
}
visit($_) for sort keys %g;
assign($_, $_) for reverse @stack;
my %groups;
for my $i (0..$#connected) {
my $id = $g{$connected[$i]};
push @{$groups{$id}}, $h{$i};
}
say join ' ', @{$groups{$_}} for sort keys %groups;
}
my %test1 = (
0 => [1],
1 => [2],
2 => [0],
3 => [1, 2, 4],
4 => [3, 5],
5 => [2, 6],
6 => [5],
7 => [4, 6, 7]
);
my %test2 = (
'Andy' => ['Bart'],
'Bart' => ['Carl'],
'Carl' => ['Andy'],
'Dave' => [<Bart Carl Earl>],
'Earl' => [<Dave Fred>],
'Fred' => [<Carl Gary>],
'Gary' => ['Fred'],
'Hank' => [<Earl Gary Hank>]
);
kosaraju(%test1);
say '';
kosaraju(%test2);
- Output:
0 1 2 3 4 5 6 7 Andy Bart Carl Dave Earl Fred Gary Hank
Phix
with javascript_semantics sequence visited, l, t, c procedure visit(sequence g, integer u) if not visited[u] then visited[u] = true for i=1 to length(g[u]) do integer v = g[u][i] visit(g,v) t[v] = deep_copy(t[v]) & u end for l &= u end if end procedure procedure assign(integer u, root=u) if visited[u] then visited[u] = false c[u] = root for v=1 to length(t[u]) do assign(t[u][v], root) end for end if end procedure function korasaju(sequence g) integer len = length(g) visited = repeat(false,len) l = {} t = repeat({},len) for u=1 to len do visit(g,u) end for c = repeat(0,len) for u=length(l) to 1 by -1 do assign(l[u]) end for return c end function constant g = {{2}, {3}, {1}, {2, 3, 5}, {4, 6}, {3, 7}, {6}, {5, 7, 8}} ?korasaju(g)
- Output:
{1,1,1,4,4,6,6,8}
Python
Works with Python 2
def kosaraju(g):
class nonlocal: pass
# 1. For each vertex u of the graph, mark u as unvisited. Let l be empty.
size = len(g)
vis = [False]*size # vertexes that have been visited
l = [0]*size
nonlocal.x = size
t = [[]]*size # transpose graph
def visit(u):
if not vis[u]:
vis[u] = True
for v in g[u]:
visit(v)
t[v] = t[v] + [u]
nonlocal.x = nonlocal.x - 1
l[nonlocal.x] = u
# 2. For each vertex u of the graph do visit(u)
for u in range(len(g)):
visit(u)
c = [0]*size
def assign(u, root):
if vis[u]:
vis[u] = False
c[u] = root
for v in t[u]:
assign(v, root)
# 3: For each element u of l in order, do assign(u, u)
for u in l:
assign(u, u)
return c
g = [[1], [2], [0], [1,2,4], [3,5], [2,6], [5], [4,6,7]]
print kosaraju(g)
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
Syntax requirements have changed. This version works with Python 3.
def kosaraju(g):
size = len(g)
vis = [False] * size
l = [0] * size
x = size
t = [[] for _ in range(size)]
def visit(u):
nonlocal x
if not vis[u]:
vis[u] = True
for v in g[u]:
visit(v)
t[v].append(u)
x -= 1
l[x] = u
for u in range(size):
visit(u)
c = [0] * size
def assign(u, root):
if vis[u]:
vis[u] = False
c[u] = root
for v in t[u]:
assign(v, root)
for u in l:
assign(u, u)
return c
g = [[1], [2], [0], [1, 2, 4], [3, 5], [2, 6], [5], [4, 6, 7]]
print(kosaraju(g))
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
Racket
#lang racket
(require racket/dict)
;; G is a dictionary of vertex -> (list vertex)
(define (Kosuraju G)
(letrec
((vertices (remove-duplicates (append (dict-keys G) (append* (dict-values G)))))
(visited?-dict (make-hash)) ; or any mutable dict type
(assigned-dict (make-hash)) ; or any mutable dict type
(neighbours:in (λ (u) (for/list (([v outs] (in-dict G)) #:when (member u outs)) v)))
(visit! (λ (u L)
(cond [(dict-ref visited?-dict u #f) L]
[else (dict-set! visited?-dict u #t)
(cons u (for/fold ((L L)) ((v (in-list (dict-ref G u)))) (visit! v L)))])))
(assign! (λ (u root)
(unless (dict-ref assigned-dict u #f)
(dict-set! assigned-dict u root)
(for ((v (in-list (neighbours:in u)))) (assign! v root)))))
(L (for/fold ((l null)) ((u (in-dict-keys G))) (visit! u l))))
(for ((u (in-list L))) (assign! u u))
(map (curry map car) (group-by cdr (dict->list assigned-dict) =))))
(module+ test
(Kosuraju '((0 1)
(2 0)
(5 2 6)
(6 5)
(1 2)
(3 1 2 4) ; equvalent to (3 . (1 2 4))
(4 5 3)
(7 4 7 6))))
- Output:
'((7) (6 5) (4 3) (2 1 0))
Raku
(formerly Perl 6)
Inspired by Python & Kotlin entries.
Accepts a hash of lists/arrays holding the vertex (name => (neighbors)) pairs. No longer limited to continuous, positive, integer vertex names.
sub kosaraju (%k) {
my %g = %k.keys.sort Z=> flat ^%k;
my %h = %g.invert;
my %visited;
my @stack;
my @transpose;
my @connected;
sub visit ($u) {
unless %visited{$u} {
%visited{$u} = True;
for |%k{$u} -> $v {
visit($v);
@transpose[%g{$v}].push: $u;
}
@stack.push: $u;
}
}
sub assign ($u, $root) {
if %visited{$u} {
%visited{$u} = False;
@connected[%g{$u}] = $root;
assign($_, $root) for |@transpose[%g{$u}];
}
}
.&visit for %g.keys;
assign($_, $_) for @stack.reverse;
(|%g{@connected}).pairs.categorize( *.value, :as(*.key) ).values.map: { %h{|$_} };
}
# TESTING
-> $test { say "\nStrongly connected components: ", |kosaraju($test).sort } for
# Same test data as all other entries, converted to a hash of lists
(((1),(2),(0),(1,2,4),(3,5),(2,6),(5),(4,6,7)).pairs.hash),
# Same layout test data with named vertices instead of numbered.
(
%(:Andy<Bart>,
:Bart<Carl>,
:Carl<Andy>,
:Dave<Bart Carl Earl>,
:Earl<Dave Fred>,
:Fred<Carl Gary>,
:Gary<Fred>,
:Hank<Earl Gary Hank>)
)
- Output:
Strongly connected components: (0 1 2)(3 4)(5 6)(7) Strongly connected components: (Andy Bart Carl)(Dave Earl)(Fred Gary)(Hank)
Sidef
func korasaju(Array g) {
# 1. For each vertex u of the graph, mark u as unvisited. Let L be empty.
var vis = g.len.of(false)
var L = []
var x = g.end
var t = g.len.of { [] }
# Recursive
func visit(u) {
if (!vis[u]) {
vis[u] = true
g[u].each {|v|
visit(v)
t[v] << u
}
L[x--] = u
}
}
# 2. For each vertex u of the graph do visit(u)
g.range.each {|u|
visit(u)
}
var c = []
# 3. Recursive subroutine:
func assign(u, root) {
if (vis[u]) {
vis[u] = false
c[u] = root
t[u].each {|v|
assign(v, root)
}
}
}
# 3. For each element u of L in order, do assign(u, u)
L.each {|u|
assign(u, u)
}
return c
}
var g = [[1], [2], [0], [1, 2, 4], [3, 5], [2, 6], [5], [4, 6, 7]]
say korasaju(g)
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
Standard ML
datatype 'a node = Node of 'a * bool ref * 'a node list ref * 'a node list ref
fun node x = Node (x, ref false, ref nil, ref nil)
fun mark (Node (_, r, _, _)) = !r before r := true
fun unmark (Node (_, r, _, _)) = !r before r := false
fun value (Node (x, _, _, _)) = x
fun sources (Node (_, _, ref xs, _)) = xs
fun targets (Node (_, _, _, ref ys)) = ys
fun connect (m, n) =
let
val Node (_, _, _, ns) = m
val Node (_, _, ms, _) = n
in
ms := m :: !ms;
ns := n :: !ns
end
datatype 'a step = One of 'a | Many of 'a list
fun visit (ms, nil) = ms
| visit (ms, One m :: ss) = visit (m :: ms, ss)
| visit (ms, Many nil :: ss) = visit (ms, ss)
| visit (ms, Many (n :: ns) :: ss) =
if mark n then
visit (ms, Many ns :: ss)
else
visit (ms, Many (targets n) :: One n :: Many ns :: ss)
fun assign (xs, nil) = xs
| assign (xs, nil :: ss) = assign (xs, ss)
| assign (xs, (n :: ns) :: ss) =
if unmark n then
assign (value n :: xs, sources n :: ns :: ss)
else
assign (xs, ns :: ss)
fun assigns (xs, nil) = xs
| assigns (xs, n :: ns) =
if unmark n then
let
val x = sources n :: nil
val x = value n :: assign (nil, x)
in
assigns (x :: xs, ns)
end
else
assigns (xs, ns)
fun kosaraju xs = assigns (nil, visit (nil, Many xs :: nil))
fun make (n, is, ijs) =
let
val xs = Vector.tabulate (n, node)
fun item i = Vector.sub (xs, i)
fun step (i, j) = connect (item i, item j)
fun path (i :: j :: js) = (step (i, j); path (j :: js))
| path _ = ()
in
map item is before app path ijs
end
val is = 0 :: nil
val ijs =
[0, 1, 2, 0, 3, 4, 0, 5, 7] ::
[0, 9, 10, 11, 12, 9, 11] ::
[1, 12] ::
[3, 5, 6, 7, 8, 6, 15] ::
[5, 13, 14, 13, 15] ::
[8, 15] ::
[10, 13] ::
nil
val ns = make (16, is, ijs)
val xs = kosaraju ns
- Output:
> xs; val it = [[15], [13, 14], [9, 10, 11, 12], [6, 7, 8], [5], [0, 1, 2, 3, 4]]: int list list
Swift
func kosaraju(graph: [[Int]]) -> [Int] {
let size = graph.count
var x = size
var vis = [Bool](repeating: false, count: size)
var l = [Int](repeating: 0, count: size)
var c = [Int](repeating: 0, count: size)
var t = [[Int]](repeating: [], count: size)
func visit(_ u: Int) {
guard !vis[u] else {
return
}
vis[u] = true
for v in graph[u] {
visit(v)
t[v].append(u)
}
x -= 1
l[x] = u
}
for u in 0..<graph.count {
visit(u)
}
func assign(_ u: Int, root: Int) {
guard vis[u] else {
return
}
vis[u] = false
c[u] = root
for v in t[u] {
assign(v, root: root)
}
}
for u in l {
assign(u, root: u)
}
return c
}
let graph = [
[1],
[2],
[0],
[1, 2, 4],
[3, 5],
[2, 6],
[5],
[4, 6, 7]
]
print(kosaraju(graph: graph))
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
Visual Basic .NET
Module Module1
Function Kosaraju(g As List(Of List(Of Integer))) As List(Of Integer)
Dim size = g.Count
Dim vis(size - 1) As Boolean
Dim l(size - 1) As Integer
Dim x = size
Dim t As New List(Of List(Of Integer))
For i = 1 To size
t.Add(New List(Of Integer))
Next
Dim visit As Action(Of Integer) = Sub(u As Integer)
If Not vis(u) Then
vis(u) = True
For Each v In g(u)
visit(v)
t(v).Add(u)
Next
x -= 1
l(x) = u
End If
End Sub
For i = 1 To size
visit(i - 1)
Next
Dim c(size - 1) As Integer
Dim assign As Action(Of Integer, Integer) = Sub(u As Integer, root As Integer)
If vis(u) Then
vis(u) = False
c(u) = root
For Each v In t(u)
assign(v, root)
Next
End If
End Sub
For Each u In l
assign(u, u)
Next
Return c.ToList
End Function
Sub Main()
Dim g = New List(Of List(Of Integer)) From {
New List(Of Integer) From {1},
New List(Of Integer) From {2},
New List(Of Integer) From {0},
New List(Of Integer) From {1, 2, 4},
New List(Of Integer) From {3, 5},
New List(Of Integer) From {2, 6},
New List(Of Integer) From {5},
New List(Of Integer) From {4, 6, 7}
}
Dim output = Kosaraju(g)
Console.WriteLine("[{0}]", String.Join(", ", output))
End Sub
End Module
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
Wren
var kosaraju = Fn.new { |g|
var gc = g.count
// 1. For each vertex u of the graph, mark u as unvisited. Let l be empty.
var vis = List.filled(gc, false)
var l = List.filled(gc, 0)
var x = gc // index for filling l in reverse order
var t = List.filled(gc, null) // transpose graph
for (i in 0...gc) t[i] = []
var visit // recursive function
visit = Fn.new { |u|
if (!vis[u]) {
vis[u] = true
for (v in g[u]) {
visit.call(v)
t[v].add(u) // construct transpose
}
x = x - 1
l[x] = u
}
}
// 2. For each vertex u of the graph do visit.call(u).
for (i in 0...gc) visit.call(i)
var c = List.filled(gc, 0) // result, the component assignment
var assign // recursive function
assign = Fn.new { |u, root|
if (vis[u]) { // repurpose vis to mean 'unassigned'
vis[u] = false
c[u] = root
for (v in t[u]) assign.call(v, root)
}
}
// 3: For each element u of l in order, do assign.call(u,u).
for (u in l) assign.call(u, u)
return c
}
var g = [ [1], [2], [0], [1, 2, 4], [3, 5], [2, 6], [5], [4, 6, 7] ]
System.print(kosaraju.call(g))
- Output:
[0, 0, 0, 3, 3, 5, 5, 7]
zkl
const VISITED=0,ASSIGNED=1;
fcn visit(u,G,L){ // u is ((visited,assigned), (id,edges))
u0:=u[0];
if(u0[VISITED]) return();
u0[VISITED]=True;
foreach idx in (u[1][1,*]){ visit(G[idx],G,L) } // vist out-neighbours
L.insert(0,u); // prepend u to L
}
fcn assign(u,root,G){ // u as above, root is a list of strong components
u0:=u[0];
if(u0[ASSIGNED]) return();
root.append(u[1][0]);
u0[ASSIGNED]=True;
uid:=u[1][0];
foreach v in (G){ // traverse graph to find in-neighbours, fugly
n,ins := v[1][0],v[1][1,*];
if(ins.holds(uid)) assign(G[n],root,G); // assign in-neighbour
}
}
fcn kosaraju(graph){ // Use Tarjan's algorithm instead of this one
// input: graph G = (V, Es)
// output: set of strongly connected components (sets of vertices)
// convert graph to ( (index,lowlink,onStack),(id,links)), ...)
// sorted by id
G:=List.createLong(graph.len(),0);
foreach v in (graph){ G[v[0]]=T( List(False,False),v) }
L:=List();
foreach u in (G){ visit(u,G,L) }
components:=List.createLong(graph.len(),List.copy,True);
foreach u in (L){ assign(u,components[u[1][0]],G) }
components=components.filter();
println("List of strongly connected components:");
foreach c in (components){ println(c.reverse().concat(",")) }
return(components);
}
// graph from https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
// with vertices id zero based (vs 1 based in article)
// ids start at zero and are consecutive (no holes), graph is unsorted
graph:= // ( (id, links/Edges), ...)
T( T(0,1), T(2,0), T(5,2,6), T(6,5),
T(1,2), T(3,1,2,4), T(4,5,3), T(7,4,7,6) );
kosaraju(graph);
- Output:
List of strongly connected components: 1,2,0 4,3 6,5 7