Kosaraju: Difference between revisions
(Go solution) |
|||
Line 81: | Line 81: | ||
} |
} |
||
}</lang> |
}</lang> |
||
=={{header|Go}}== |
|||
<lang 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 |
|||
}</lang> |
|||
{{out}} |
|||
<pre> |
|||
[0 0 0 3 3 5 5 7] |
|||
</pre> |
|||
=={{header|Racket}}== |
=={{header|Racket}}== |
Revision as of 19:11, 14 February 2017
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.
- References
- The article on Wikipedia.
C#
<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); } }</lang>
Go
<lang 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
}</lang>
- Output:
[0 0 0 3 3 5 5 7]
Racket
<lang 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))))</lang>
- Output:
'((7) (6 5) (4 3) (2 1 0))
zkl
<lang 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);
}</lang> <lang zkl> // 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);</lang>
- Output:
List of strongly connected components: 1,2,0 4,3 6,5 7