Skip to content

Commit aebac89

Browse files
authored
Add graph traversal algorithms (TheAlgorithms#236)
* Add DFS algorithm * Add BFS algorithm * Update README.md
1 parent 803d29f commit aebac89

10 files changed

+417
-3
lines changed

Algorithms.Tests/Algorithms.Tests.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>net5.0</TargetFramework>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
using Algorithms.Graph;
2+
using DataStructures.Graph;
3+
using NUnit.Framework;
4+
using System.Collections.Generic;
5+
6+
namespace Algorithms.Tests.Graph
7+
{
8+
public class BreadthFirstSearchTests
9+
{
10+
[Test]
11+
public void VisitAll_ShouldCountNumberOfVisitedVertix_ResultShouldBeTheSameAsNumberOfVerticesInGraph()
12+
{
13+
//Arrange
14+
var graph = new DirectedWeightedGraph<int>(10);
15+
16+
var vertex1 = graph.AddVertex(1);
17+
18+
var vertex2 = graph.AddVertex(20);
19+
20+
var vertex3 = graph.AddVertex(40);
21+
22+
var vertex4 = graph.AddVertex(40);
23+
24+
graph.AddEdge(vertex1, vertex2, 1);
25+
26+
graph.AddEdge(vertex2, vertex3, 1);
27+
28+
graph.AddEdge(vertex2, vertex4, 1);
29+
30+
graph.AddEdge(vertex4, vertex1, 1);
31+
32+
var dfsSearcher = new BreadthFirstSearch<int>();
33+
34+
long countOfVisitedVertices = 0;
35+
36+
//Act
37+
dfsSearcher.VisitAll(graph, vertex1, (Vertex<int> vertex) => countOfVisitedVertices++);
38+
39+
//Assert
40+
Assert.AreEqual(countOfVisitedVertices, graph.Count);
41+
}
42+
43+
[Test]
44+
public void VisitAll_ShouldCountNumberOfVisitedVerices_TwoSeparatedGraphInOne()
45+
{
46+
//Arrange
47+
var graph = new DirectedWeightedGraph<int>(10);
48+
49+
var vertex1 = graph.AddVertex(1);
50+
51+
var vertex2 = graph.AddVertex(20);
52+
53+
var vertex3 = graph.AddVertex(40);
54+
55+
var vertex4 = graph.AddVertex(40);
56+
57+
var vertex5 = graph.AddVertex(40);
58+
59+
var vertex6 = graph.AddVertex(40);
60+
61+
graph.AddEdge(vertex1, vertex2, 1);
62+
63+
graph.AddEdge(vertex2, vertex3, 1);
64+
65+
graph.AddEdge(vertex4, vertex5, 1);
66+
67+
graph.AddEdge(vertex5, vertex6, 1);
68+
69+
var dfsSearcher = new BreadthFirstSearch<int>();
70+
71+
long countOfVisitedVerticesPerFirstGraph = 0;
72+
73+
long countOfVisitedVerticesPerSecondGraph = 0;
74+
75+
//Act
76+
dfsSearcher.VisitAll(graph, vertex1, (Vertex<int> vertex) => countOfVisitedVerticesPerFirstGraph++);
77+
78+
dfsSearcher.VisitAll(graph, vertex4, (Vertex<int> vertex) => countOfVisitedVerticesPerSecondGraph++);
79+
80+
//Assert
81+
Assert.AreEqual(countOfVisitedVerticesPerFirstGraph, 3);
82+
83+
Assert.AreEqual(countOfVisitedVerticesPerSecondGraph, 3);
84+
}
85+
86+
[Test]
87+
public void VisitAll_ReturnTheSuqenceOfVertices_ShouldBeTheSameAsExpected()
88+
{
89+
//Arrange
90+
var graph = new DirectedWeightedGraph<int>(10);
91+
92+
var vertex1 = graph.AddVertex(1);
93+
94+
var vertex2 = graph.AddVertex(20);
95+
96+
var vertex3 = graph.AddVertex(40);
97+
98+
var vertex4 = graph.AddVertex(40);
99+
100+
var vertex5 = graph.AddVertex(40);
101+
102+
graph.AddEdge(vertex1, vertex2, 1);
103+
104+
graph.AddEdge(vertex1, vertex5, 1);
105+
106+
graph.AddEdge(vertex2, vertex3, 1);
107+
108+
graph.AddEdge(vertex2, vertex5, 1);
109+
110+
graph.AddEdge(vertex2, vertex4, 1);
111+
112+
var dfsSearcher = new BreadthFirstSearch<int>();
113+
114+
var expectedSequenceOfVisitedVertices = new List<Vertex<int>>
115+
{
116+
vertex1,
117+
vertex2,
118+
vertex5,
119+
vertex3,
120+
vertex4
121+
};
122+
123+
var sequenceOfVisitedVertices = new List<Vertex<int>>();
124+
125+
//Act
126+
dfsSearcher.VisitAll(graph, vertex1, (Vertex<int> vertex) => sequenceOfVisitedVertices.Add(vertex));
127+
128+
//Assert
129+
CollectionAssert.AreEqual(expectedSequenceOfVisitedVertices, sequenceOfVisitedVertices);
130+
}
131+
}
132+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using Algorithms.Graph;
2+
using DataStructures.Graph;
3+
using NUnit.Framework;
4+
using System.Collections.Generic;
5+
6+
namespace Algorithms.Tests.Graph
7+
{
8+
public class DepthFirstSearchTests
9+
{
10+
[Test]
11+
public void VisitAll_ShouldCountNumberOfVisitedVertix_ResultShouldBeTheSameAsNumberOfVerticesInGraph()
12+
{
13+
//Arrange
14+
var graph = new DirectedWeightedGraph<int>(10);
15+
16+
var vertex1 = graph.AddVertex(1);
17+
18+
var vertex2 = graph.AddVertex(20);
19+
20+
var vertex3 = graph.AddVertex(40);
21+
22+
var vertex4 = graph.AddVertex(40);
23+
24+
graph.AddEdge(vertex1, vertex2, 1);
25+
26+
graph.AddEdge(vertex2, vertex3, 1);
27+
28+
graph.AddEdge(vertex2, vertex4, 1);
29+
30+
graph.AddEdge(vertex4, vertex1, 1);
31+
32+
var dfsSearcher = new DepthFirstSearch<int>();
33+
34+
long countOfVisitedVertices = 0;
35+
36+
//Act
37+
dfsSearcher.VisitAll(graph, vertex1, (Vertex<int> vertex) => countOfVisitedVertices++);
38+
39+
//Assert
40+
Assert.AreEqual(countOfVisitedVertices, graph.Count);
41+
}
42+
43+
[Test]
44+
public void VisitAll_ShouldCountNumberOfVisitedVerices_TwoSeparatedGraphInOne()
45+
{
46+
//Arrange
47+
var graph = new DirectedWeightedGraph<int>(10);
48+
49+
var vertex1 = graph.AddVertex(1);
50+
51+
var vertex2 = graph.AddVertex(20);
52+
53+
var vertex3 = graph.AddVertex(40);
54+
55+
var vertex4 = graph.AddVertex(40);
56+
57+
var vertex5 = graph.AddVertex(40);
58+
59+
var vertex6 = graph.AddVertex(40);
60+
61+
graph.AddEdge(vertex1, vertex2, 1);
62+
63+
graph.AddEdge(vertex2, vertex3, 1);
64+
65+
graph.AddEdge(vertex4, vertex5, 1);
66+
67+
graph.AddEdge(vertex5, vertex6, 1);
68+
69+
var dfsSearcher = new DepthFirstSearch<int>();
70+
71+
long countOfVisitedVerticesPerFirstGraph = 0;
72+
73+
long countOfVisitedVerticesPerSecondGraph = 0;
74+
75+
//Act
76+
dfsSearcher.VisitAll(graph, vertex1, (Vertex<int> vertex) => countOfVisitedVerticesPerFirstGraph++);
77+
78+
dfsSearcher.VisitAll(graph, vertex4, (Vertex<int> vertex) => countOfVisitedVerticesPerSecondGraph++);
79+
80+
//Assert
81+
Assert.AreEqual(countOfVisitedVerticesPerFirstGraph, 3);
82+
83+
Assert.AreEqual(countOfVisitedVerticesPerSecondGraph, 3);
84+
}
85+
86+
[Test]
87+
public void VisitAll_ReturnTheSuqenceOfVertices_ShouldBeTheSameAsExpected()
88+
{
89+
//Arrange
90+
var graph = new DirectedWeightedGraph<int>(10);
91+
92+
var vertex1 = graph.AddVertex(1);
93+
94+
var vertex2 = graph.AddVertex(20);
95+
96+
var vertex3 = graph.AddVertex(40);
97+
98+
var vertex4 = graph.AddVertex(40);
99+
100+
var vertex5 = graph.AddVertex(40);
101+
102+
graph.AddEdge(vertex1, vertex2, 1);
103+
104+
graph.AddEdge(vertex2, vertex3, 1);
105+
106+
graph.AddEdge(vertex2, vertex4, 1);
107+
108+
graph.AddEdge(vertex3, vertex5, 1);
109+
110+
var dfsSearcher = new DepthFirstSearch<int>();
111+
112+
var expectedSequenceOfVisitedVertices = new List<Vertex<int>>
113+
{
114+
vertex1,
115+
vertex2,
116+
vertex3,
117+
vertex5,
118+
vertex4
119+
};
120+
121+
var sequenceOfVisitedVertices = new List<Vertex<int>>();
122+
123+
//Act
124+
dfsSearcher.VisitAll(graph, vertex1, (Vertex<int> vertex) => sequenceOfVisitedVertices.Add(vertex));
125+
126+
//Assert
127+
CollectionAssert.AreEqual(expectedSequenceOfVisitedVertices, sequenceOfVisitedVertices);
128+
}
129+
}
130+
}

Algorithms/Algorithms.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
</ItemGroup>
2525

2626
<ItemGroup>
27+
<ProjectReference Include="..\DataStructures\DataStructures.csproj" />
2728
<ProjectReference Include="..\Utilities\Utilities.csproj" />
2829
</ItemGroup>
2930

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using DataStructures.Graph;
4+
5+
namespace Algorithms.Graph
6+
{
7+
/// <summary>
8+
/// Breadth First Search - algorithm for traversing graph.
9+
/// Algorithm starts from root node that is selected by the user.
10+
/// Algorithm explores all nodes at the present depth.
11+
/// </summary>
12+
/// <typeparam name="T">Vertex data type.</typeparam>
13+
public class BreadthFirstSearch<T> : IGraphSearch<T> where T : IComparable<T>
14+
{
15+
/// <summary>
16+
/// Traverses graph from start vertex.
17+
/// </summary>
18+
/// <param name="graph">Graph instance.</param>
19+
/// <param name="startVertex">Vertex that search starts from.</param>
20+
/// <param name="action">Action that needs to be executed on each graph vertex.</param>
21+
public void VisitAll(IDirectedWeightedGraph<T> graph, Vertex<T> startVertex, Action<Vertex<T>>? action = default)
22+
{
23+
Bfs(graph, startVertex, action, new HashSet<Vertex<T>>());
24+
}
25+
26+
/// <summary>
27+
/// Traverses graph from start vertex.
28+
/// </summary>
29+
/// <param name="graph">Graph instance.</param>
30+
/// <param name="startVertex">Vertex that search starts from.</param>
31+
/// <param name="action">Action that needs to be executed on each graph vertex.</param>
32+
/// <param name="visited">Hash set with visited vertices.</param>
33+
private void Bfs(IDirectedWeightedGraph<T> graph, Vertex<T> startVertex, Action<Vertex<T>>? action, HashSet<Vertex<T>> visited)
34+
{
35+
var queue = new Queue<Vertex<T>>();
36+
37+
queue.Enqueue(startVertex);
38+
39+
while (queue.Count > 0)
40+
{
41+
var currentVertex = queue.Dequeue();
42+
43+
if (currentVertex == null || visited.Contains(currentVertex))
44+
{
45+
continue;
46+
}
47+
48+
foreach (var vertex in graph.GetNeighbors(currentVertex))
49+
{
50+
queue.Enqueue(vertex!);
51+
}
52+
53+
action?.Invoke(currentVertex);
54+
55+
visited.Add(currentVertex);
56+
}
57+
}
58+
}
59+
}

Algorithms/Graph/DepthFirstSearch.cs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using DataStructures.Graph;
4+
5+
namespace Algorithms.Graph
6+
{
7+
/// <summary>
8+
/// Depth First Search - algorithm for traversing graph.
9+
/// Algorithm starts from root node that is selected by the user.
10+
/// Algorithm explores as far as possible along each branch before backtracking.
11+
/// </summary>
12+
/// <typeparam name="T">Vertex data type.</typeparam>
13+
public class DepthFirstSearch<T> : IGraphSearch<T> where T : IComparable<T>
14+
{
15+
/// <summary>
16+
/// Traverses graph from start vertex.
17+
/// </summary>
18+
/// <param name="graph">Graph instance.</param>
19+
/// <param name="startVertex">Vertex that search starts from.</param>
20+
/// <param name="action">Action that needs to be executed on each graph vertex.</param>
21+
public void VisitAll(IDirectedWeightedGraph<T> graph, Vertex<T> startVertex, Action<Vertex<T>>? action = default)
22+
{
23+
Dfs(graph, startVertex, action, new HashSet<Vertex<T>>());
24+
}
25+
26+
/// <summary>
27+
/// Traverses graph from start vertex.
28+
/// </summary>
29+
/// <param name="graph">Graph instance.</param>
30+
/// <param name="startVertex">Vertex that search starts from.</param>
31+
/// <param name="action">Action that needs to be executed on each graph vertex.</param>
32+
/// <param name="visited">Hash set with visited vertices.</param>
33+
private void Dfs(IDirectedWeightedGraph<T> graph, Vertex<T> startVertex, Action<Vertex<T>>? action, HashSet<Vertex<T>> visited)
34+
{
35+
action?.Invoke(startVertex);
36+
37+
visited.Add(startVertex);
38+
39+
foreach (var vertex in graph.GetNeighbors(startVertex))
40+
{
41+
if (vertex == null || visited.Contains(vertex))
42+
{
43+
continue;
44+
}
45+
46+
Dfs(graph, vertex!, action, visited);
47+
}
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)