Skip to content

Commit 98dfa37

Browse files
dideldevdiegodm-223diego
authored
Add Kosaraju Algorithm (#303)
Co-authored-by: Diego Delgado <diegodm223@outlook.com> Co-authored-by: diego <diego@ibertest.es>
1 parent cba779c commit 98dfa37

File tree

3 files changed

+231
-0
lines changed

3 files changed

+231
-0
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using Algorithms.Graph;
2+
using DataStructures.Graph;
3+
using NUnit.Framework;
4+
using FluentAssertions;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
8+
namespace Algorithms.Tests.Graph
9+
{
10+
public class KosarajuTests
11+
{
12+
13+
[Test]
14+
public void GetRepresentativesTest()
15+
{
16+
// Create a graph with some SCC.
17+
var graph = new DirectedWeightedGraph<int>(10);
18+
19+
var vertex1 = graph.AddVertex(1);
20+
var vertex2 = graph.AddVertex(2);
21+
var vertex3 = graph.AddVertex(3);
22+
var vertex4 = graph.AddVertex(4);
23+
var vertex5 = graph.AddVertex(5);
24+
var vertex6 = graph.AddVertex(6);
25+
var vertex7 = graph.AddVertex(7);
26+
27+
graph.AddEdge(vertex1, vertex2, 1);
28+
graph.AddEdge(vertex2, vertex3, 1);
29+
graph.AddEdge(vertex3, vertex1, 1);
30+
graph.AddEdge(vertex3, vertex2, 1);
31+
graph.AddEdge(vertex2, vertex4, 1);
32+
graph.AddEdge(vertex4, vertex5, 1);
33+
graph.AddEdge(vertex5, vertex4, 1);
34+
graph.AddEdge(vertex5, vertex6, 1);
35+
36+
// Run the agorithm and obtain the representative vertex of the SCC to which each vertex belongs.
37+
Dictionary<Vertex<int>,Vertex<int>> result = Kosaraju<int>.GetRepresentatives(graph);
38+
39+
// Check every Vertex belongs to a SCC
40+
result.Should().ContainKey(vertex1);
41+
result.Should().ContainKey(vertex2);
42+
result.Should().ContainKey(vertex3);
43+
result.Should().ContainKey(vertex4);
44+
result.Should().ContainKey(vertex5);
45+
result.Should().ContainKey(vertex6);
46+
result.Should().ContainKey(vertex7);
47+
48+
// There should be 4 SCC: {1,2,3}, {4,5}, {6} and {7}
49+
// Vertices 1, 2 and 3 are a SCC
50+
result[vertex1].Should().Be(result[vertex2]).And.Be(result[vertex3]);
51+
52+
// Vertices 4 and 5 are another SCC
53+
result[vertex4].Should().Be(result[vertex5]);
54+
55+
// And the should have a different representative vertex
56+
result[vertex1].Should().NotBe(result[vertex4]);
57+
58+
// Vertices 6 and 7 are their own SCC
59+
result[vertex6].Should().Be(vertex6);
60+
result[vertex7].Should().Be(vertex7);
61+
}
62+
63+
[Test]
64+
public void GetSccTest()
65+
{
66+
// Create a graph with some SCC.
67+
var graph = new DirectedWeightedGraph<int>(10);
68+
69+
var vertex1 = graph.AddVertex(1);
70+
var vertex2 = graph.AddVertex(2);
71+
var vertex3 = graph.AddVertex(3);
72+
var vertex4 = graph.AddVertex(4);
73+
var vertex5 = graph.AddVertex(5);
74+
var vertex6 = graph.AddVertex(6);
75+
var vertex7 = graph.AddVertex(7);
76+
77+
graph.AddEdge(vertex1, vertex2, 1);
78+
graph.AddEdge(vertex2, vertex3, 1);
79+
graph.AddEdge(vertex3, vertex1, 1);
80+
graph.AddEdge(vertex3, vertex2, 1);
81+
graph.AddEdge(vertex2, vertex4, 1);
82+
graph.AddEdge(vertex4, vertex5, 1);
83+
graph.AddEdge(vertex5, vertex4, 1);
84+
graph.AddEdge(vertex5, vertex6, 1);
85+
86+
// Run the algorithm and get SCC as lists of vertices.
87+
var scc = Kosaraju<int>.GetScc(graph);
88+
89+
// There should be 4 SCC: {1,2,3}, {4,5}, {6} and {7}
90+
scc.Should().HaveCount(4);
91+
92+
// Vertices 1, 2 and 3 are a SCC
93+
scc.First(c => c.Contains(vertex1)).Should().Contain(vertex2).And.Contain(vertex3);
94+
95+
// Vertices 4 and 5 are another SCC
96+
scc.First(c => c.Contains(vertex4)).Should().Contain(vertex5);
97+
98+
// Vertices 6 and 7 are their own SCC
99+
scc.First(c => c.Contains(vertex6)).Should().HaveCount(1);
100+
scc.First(c => c.Contains(vertex7)).Should().HaveCount(1);
101+
}
102+
}
103+
}

Algorithms/Graph/Kosaraju.cs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using DataStructures.Graph;
4+
5+
namespace Algorithms.Graph
6+
{
7+
/// <summary>
8+
/// Implementation of Kosaraju-Sharir's algorithm (also known as Kosaraju's algorithm) to find the
9+
/// strongly connected components (SCC) of a directed graph.
10+
/// See https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm.
11+
/// </summary>
12+
/// <typeparam name="T">Vertex data type.</typeparam>
13+
public static class Kosaraju<T>
14+
{
15+
/// <summary>
16+
/// First DFS for Kosaraju algorithm: traverse the graph creating a reverse order explore list <paramref name="reversed"/>.
17+
/// </summary>
18+
/// <param name="v">Vertex to explore.</param>
19+
/// <param name="graph">Graph instance.</param>
20+
/// <param name="visited">List of already visited vertex.</param>
21+
/// <param name="reversed">Reversed list of vertex for the second DFS.</param>
22+
public static void Visit(Vertex<T> v, IDirectedWeightedGraph<T> graph, HashSet<Vertex<T>> visited, Stack<Vertex<T>> reversed)
23+
{
24+
if (visited.Contains(v))
25+
{
26+
return;
27+
}
28+
29+
// Set v as visited
30+
visited.Add(v);
31+
32+
// Push v in the stack.
33+
// This can also be done with a List, inserting v at the begining of the list
34+
// after visit the neighbors.
35+
reversed.Push(v);
36+
37+
// Visit neighbors
38+
foreach (var u in graph.GetNeighbors(v))
39+
{
40+
Visit(u!, graph, visited, reversed);
41+
}
42+
}
43+
44+
/// <summary>
45+
/// Second DFS for Kosaraju algorithm. Traverse the graph in reversed order
46+
/// assigning a root vertex for every vertex that belong to the same SCC.
47+
/// </summary>
48+
/// <param name="v">Vertex to assign.</param>
49+
/// <param name="root">Root vertext, representative of the SCC.</param>
50+
/// <param name="graph">Graph with vertex and edges.</param>
51+
/// <param name="roots">
52+
/// Dictionary that assigns to each vertex the root of the SCC to which it corresponds.
53+
/// </param>
54+
public static void Assign(Vertex<T> v, Vertex<T> root, IDirectedWeightedGraph<T> graph, Dictionary<Vertex<T>, Vertex<T>> roots)
55+
{
56+
// If v already has a representative vertex (root) already assigned, do nothing.
57+
if (roots.ContainsKey(v))
58+
{
59+
return;
60+
}
61+
62+
// Assign the root to the vertex.
63+
roots.Add(v, root);
64+
65+
// Assign the current root vertex to v neighbors.
66+
foreach (var u in graph.GetNeighbors(v))
67+
{
68+
Assign(u!, root, graph, roots);
69+
}
70+
}
71+
72+
/// <summary>
73+
/// Find the representative vertex of the SCC for each vertex on the graph.
74+
/// </summary>
75+
/// <param name="graph">Graph to explore.</param>
76+
/// <returns>A dictionary that assigns to each vertex a root vertex of the SCC they belong. </returns>
77+
public static Dictionary<Vertex<T>, Vertex<T>> GetRepresentatives(IDirectedWeightedGraph<T> graph)
78+
{
79+
HashSet<Vertex<T>> visited = new HashSet<Vertex<T>>();
80+
Stack<Vertex<T>> reversedL = new Stack<Vertex<T>>();
81+
Dictionary<Vertex<T>, Vertex<T>> representatives = new Dictionary<Vertex<T>, Vertex<T>>();
82+
83+
foreach (var v in graph.Vertices)
84+
{
85+
if (v != null)
86+
{
87+
Visit(v, graph, visited, reversedL);
88+
}
89+
}
90+
91+
visited.Clear();
92+
93+
while (reversedL.Count > 0)
94+
{
95+
Vertex<T> v = reversedL.Pop();
96+
Assign(v, v, graph, representatives);
97+
}
98+
99+
return representatives;
100+
}
101+
102+
/// <summary>
103+
/// Get the Strongly Connected Components for the graph.
104+
/// </summary>
105+
/// <param name="graph">Graph to explore.</param>
106+
/// <returns>An array of SCC.</returns>
107+
public static IEnumerable<Vertex<T>>[] GetScc(IDirectedWeightedGraph<T> graph)
108+
{
109+
var representatives = GetRepresentatives(graph);
110+
Dictionary<Vertex<T>, List<Vertex<T>>> scc = new Dictionary<Vertex<T>, List<Vertex<T>>>();
111+
foreach (var kv in representatives)
112+
{
113+
// Assign all vertex (key) that have the seem root (value) to a single list.
114+
if (scc.ContainsKey(kv.Value))
115+
{
116+
scc[kv.Value].Add(kv.Key);
117+
}
118+
else
119+
{
120+
scc.Add(kv.Value, new List<Vertex<T>> { kv.Key });
121+
}
122+
}
123+
124+
return scc.Values.ToArray();
125+
}
126+
}
127+
}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ This repository contains algorithms and data structures implemented in C# for ed
2727
* [BreadthFirstSearch](./Algorithms/Graph/BreadthFirstSearch.cs)
2828
* [DepthFirstSearch](./Algorithms/Graph/DepthFirstSearch.cs)
2929
* [Dijkstra Shortest Path](./Algorithms/Graph/Dijkstra/DijkstraAlgorithm.cs)
30+
* [Kosaraju](./Algorithms/Graph/Kosaraju.cs)
3031
* [Knapsack problem](./Algorithms/Knapsack)
3132
* [Naive solver](./Algorithms/Knapsack/NaiveKnapsackSolver.cs)
3233
* [Dynamic Programming solver](./Algorithms/Knapsack/DynamicProgrammingKnapsackSolver.cs)

0 commit comments

Comments
 (0)