Skip to content

Commit e69bb4a

Browse files
committed
Refactoring on DFS
Add unit test
1 parent 6f1382f commit e69bb4a

File tree

2 files changed

+67
-72
lines changed

2 files changed

+67
-72
lines changed

src/Algorithms/GraphTraversal/DepthFirstSearch.cs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,59 +9,55 @@ public static class DepthFirstSearch
99
public static IEnumerable<T> Explore<T>(T start, Func<T, IEnumerable<T>> getNeighbours, Func<T, bool> isEnd = null)
1010
{
1111
var visited = new HashSet<T>();
12-
var stack = new Stack<T>();
12+
var toVisit = new Stack<T>();
1313

14-
stack.Push(start);
14+
toVisit.Push(start);
1515

16-
while (stack.Any())
16+
while (toVisit.Any())
1717
{
18-
var current = stack.Pop();
19-
20-
if (!visited.Add(current)) { continue; }
18+
var current = toVisit.Pop();
19+
20+
visited.Add(current);
2121

2222
yield return current;
2323

2424
if (isEnd != null && isEnd(current))
2525
break;
2626

27-
var neighbours = getNeighbours(current)
28-
.Where(n => !visited.Contains(n));
27+
var neighbours = GetNotVisitiedNeighbours(getNeighbours, current, visited);
2928

30-
foreach (var neighbour in neighbours.Reverse())
31-
stack.Push(neighbour);
29+
neighbours.ForEach(toVisit.Push);
3230
}
3331

3432
}
35-
33+
3634
public static IEnumerable<T> FindPath<T>(T start, Func<T, IEnumerable<T>> getNeighbours, Func<T, bool> isEnd)
3735
{
3836
var visited = new HashSet<T>();
39-
var stack = new Stack<T>();
37+
var toVisit = new Stack<T>();
4038
var path = new Stack<T>();
4139

42-
stack.Push(start);
40+
toVisit.Push(start);
4341

44-
while (stack.Any())
42+
while (toVisit.Any())
4543
{
46-
var current = stack.Pop();
44+
var current = toVisit.Pop();
4745
path.Push(current);
4846

4947
visited.Add(current);
5048

5149
if (isEnd(current))
5250
return path.Reverse();
5351

54-
var neighbours = getNeighbours(current)
55-
.Where(n => !visited.Contains(n))
56-
.ToArray();
52+
var neighbours = GetNotVisitiedNeighbours(getNeighbours, current, visited);
5753

5854
if (!neighbours.Any())
5955
{
60-
if(!stack.Any()) { break; }
56+
if(!toVisit.Any()) { break; }
6157

6258
var lastInPath = path.Peek();
6359
var lastInPathNeighbours = getNeighbours(lastInPath);
64-
var next = stack.Peek();
60+
var next = toVisit.Peek();
6561
while (!lastInPathNeighbours.Contains(next))
6662
{
6763
path.Pop();
@@ -70,14 +66,22 @@ public static IEnumerable<T> FindPath<T>(T start, Func<T, IEnumerable<T>> getNei
7066
}
7167

7268
}
73-
74-
75-
foreach (var neighbour in neighbours.Reverse())
76-
stack.Push(neighbour);
69+
else
70+
{
71+
neighbours.ForEach(toVisit.Push);
72+
}
7773
}
7874

7975
return Enumerable.Empty<T>();
8076

8177
}
78+
79+
private static List<T> GetNotVisitiedNeighbours<T>(Func<T, IEnumerable<T>> getNeighbours, T current, HashSet<T> visited)
80+
{
81+
return getNeighbours(current)
82+
.Where(n => !visited.Contains(n))
83+
.Reverse()
84+
.ToList();
85+
}
8286
}
8387
}

tests/AlgorithmTests/GraphTraversal/DepthFirstSearchTests.cs

Lines changed: 39 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace AlgorithmTests.GraphTraversal
99
{
10-
class DepthFirstSearchTests
10+
public class DepthFirstSearchTests
1111
{
1212
[Test]
1313
public void Small_maze_test()
@@ -24,26 +24,13 @@ public void Small_maze_test()
2424
%.-----------------%
2525
%%%%%%%%%%%%%%%%%%%%";
2626

27-
Func<Point, char> getCell = p => maze.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Skip(1).ToArray()[p.Y][p.X];
28-
29-
Func<Point, IEnumerable<Point>> getNeighbours = p =>
30-
{
31-
var allPoints = new[]
32-
{
33-
new Point(p.X, p.Y - 1), // top
34-
new Point(p.X + 1, p.Y), // right
35-
new Point(p.X, p.Y + 1), // bottom
36-
new Point(p.X - 1, p.Y), // left
37-
};
38-
39-
return allPoints.Where(x => getCell(x) != '%');
40-
41-
};
27+
Func<Point, char> getCell = GetCell(maze);
28+
Func<Point, IEnumerable<Point>> getNeighbours = GetNeighbours(getCell);
4229

4330

4431
var result = DepthFirstSearch.Explore(start, getNeighbours);
4532

46-
List<Point> path = new List<Point>();
33+
var path = new List<Point>();
4734
foreach (var item in result)
4835
{
4936
path.Add(item);
@@ -110,29 +97,13 @@ public void Small_maze_FindPath_test()
11097
%.-----------------%
11198
%%%%%%%%%%%%%%%%%%%%";
11299

113-
Func<Point, char> getCell = p =>
114-
{
115-
return maze.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Skip(1).ToArray()[p.Y][p.X];
116-
};
117-
118-
Func<Point, IEnumerable<Point>> getNeighbours = p =>
119-
{
120-
var allPoints = new[]
121-
{
122-
new Point(p.X, p.Y - 1), // top
123-
new Point(p.X + 1, p.Y), // right
124-
new Point(p.X, p.Y + 1), // bottom
125-
new Point(p.X - 1, p.Y), // left
126-
};
127-
128-
return allPoints.Where(x => getCell(x) != '%');
129-
130-
};
100+
var getCell = GetCell(maze);
101+
var getNeighbours = GetNeighbours(getCell);
131102

132103

133104
var result = DepthFirstSearch.FindPath(start, getNeighbours,point => point == end ).ToArray();
134105

135-
for (int i = 0; i < result.Count() - 1; i++)
106+
for (var i = 0; i < result.Length - 1; i++)
136107
{
137108
var twoPoints = result.Skip(i).Take(2).ToArray();
138109
var point1 = twoPoints[0];
@@ -150,17 +121,14 @@ public void Tiny_maze_FindPath_test()
150121
var start = new Point(1, 3);
151122
var end = new Point(7, 3);
152123

153-
var maze = @"
124+
const string maze = @"
154125
%%%%%%%%%
155126
%-------%
156127
%%%%-%%%%
157128
%P------%
158129
%%%%%%%%%";
159130

160-
Func<Point, char> getCell = p =>
161-
{
162-
return maze.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Skip(1).ToArray()[p.Y][p.X];
163-
};
131+
Func<Point, char> getCell = p => maze.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Skip(1).ToArray()[p.Y][p.X];
164132

165133
Func<Point, IEnumerable<Point>> getNeighbours = p =>
166134
{
@@ -179,7 +147,7 @@ public void Tiny_maze_FindPath_test()
179147

180148
var result = DepthFirstSearch.FindPath(start, getNeighbours, point => point == end).ToArray();
181149

182-
for (int i = 0; i < result.Count() - 1; i++)
150+
for (var i = 0; i < result.Length - 1; i++)
183151
{
184152
var twoPoints = result.Skip(i).Take(2).ToArray();
185153
var point1 = twoPoints[0];
@@ -191,23 +159,47 @@ public void Tiny_maze_FindPath_test()
191159
Assert.That(result.Last(), Is.EqualTo(end));
192160
}
193161

162+
[Test]
163+
public void Explore_should_stop_on_specific_condition()
164+
{
165+
var start = new Point(1, 3);
166+
var end = new Point(1, 1);
167+
Func<Point,bool> endCondition = point => point == end;
168+
169+
const string maze = @"
170+
%%%%%%%%%
171+
%-------%
172+
%%%%-%%%%
173+
%P------%
174+
%%%%%%%%%";
175+
176+
var getCell = GetCell(maze);
177+
var getNeighbours = GetNeighbours(getCell);
178+
179+
var result = DepthFirstSearch
180+
.Explore(start, getNeighbours, endCondition)
181+
.ToArray();
182+
183+
Assert.That(result.Count(), Is.EqualTo(12));
184+
Assert.That(result.Last(),Is.EqualTo(end));
185+
}
186+
194187
[Test]
195188
public void FindPath_should_return_empty_enumerqble_when_no_path_found()
196189
{
197190
var start = new Point(1, 3);
198191
var end = new Point(7, 1);
199192

200-
var maze = @"
193+
const string maze = @"
201194
%%%%%%%%%
202195
%-------%
203196
%%%%%%%%%
204197
%P------%
205198
%%%%%%%%%";
206199

207-
Func<Point, char> getCell = GetCell(maze);
208-
Func<Point, IEnumerable<Point>> getNeighbours = GetNeighbours(getCell);
209-
210-
200+
var getCell = GetCell(maze);
201+
var getNeighbours = GetNeighbours(getCell);
202+
211203
var result = DepthFirstSearch.FindPath(start, getNeighbours, point => point == end);
212204

213205
Assert.That(result, Is.Empty);
@@ -226,7 +218,6 @@ private static Func<Point, IEnumerable<Point>> GetNeighbours(Func<Point, char> g
226218
};
227219

228220
return allPoints.Where(x => getCell(x) != '%');
229-
230221
};
231222
}
232223

0 commit comments

Comments
 (0)