Skip to content

Commit d0504fd

Browse files
committedMay 5, 2018
Add detect cycle.
1 parent 20bc442 commit d0504fd

File tree

4 files changed

+93
-24
lines changed

4 files changed

+93
-24
lines changed
 

‎.eslintrc

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"no-bitwise": "off",
1010
"no-lonely-if": "off",
1111
"class-methods-use-this": "off",
12-
"arrow-body-style": "off"
12+
"arrow-body-style": "off",
13+
"no-loop-func": "off"
1314
}
1415
}

‎src/algorithms/graph/breadth-first-search/__test__/breadthFirstSearch.test.js

+66-6
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,39 @@ describe('breadthFirstSearch', () => {
5454
expect(enterVertexCallback).toHaveBeenCalledTimes(8);
5555
expect(leaveVertexCallback).toHaveBeenCalledTimes(8);
5656

57-
expect(enterVertexCallback.mock.calls.toString()).toBe('A,B,D,E,C,H,F,G');
58-
expect(leaveVertexCallback.mock.calls.toString()).toBe('A,B,D,E,C,H,F,G');
57+
const enterVertexParamsMap = [
58+
{ currentVertex: vertexA, previousVertex: null },
59+
{ currentVertex: vertexB, previousVertex: vertexA },
60+
{ currentVertex: vertexD, previousVertex: vertexB },
61+
{ currentVertex: vertexE, previousVertex: vertexD },
62+
{ currentVertex: vertexC, previousVertex: vertexE },
63+
{ currentVertex: vertexH, previousVertex: vertexC },
64+
{ currentVertex: vertexF, previousVertex: vertexH },
65+
{ currentVertex: vertexG, previousVertex: vertexF },
66+
];
67+
68+
for (let callIndex = 0; callIndex < graph.getAllVertices().length; callIndex += 1) {
69+
const params = enterVertexCallback.mock.calls[callIndex][0];
70+
expect(params.currentVertex).toEqual(enterVertexParamsMap[callIndex].currentVertex);
71+
expect(params.previousVertex).toEqual(enterVertexParamsMap[callIndex].previousVertex);
72+
}
73+
74+
const leaveVertexParamsMap = [
75+
{ currentVertex: vertexA, previousVertex: null },
76+
{ currentVertex: vertexB, previousVertex: vertexA },
77+
{ currentVertex: vertexD, previousVertex: vertexB },
78+
{ currentVertex: vertexE, previousVertex: vertexD },
79+
{ currentVertex: vertexC, previousVertex: vertexE },
80+
{ currentVertex: vertexH, previousVertex: vertexC },
81+
{ currentVertex: vertexF, previousVertex: vertexH },
82+
{ currentVertex: vertexG, previousVertex: vertexF },
83+
];
84+
85+
for (let callIndex = 0; callIndex < graph.getAllVertices().length; callIndex += 1) {
86+
const params = leaveVertexCallback.mock.calls[callIndex][0];
87+
expect(params.currentVertex).toEqual(leaveVertexParamsMap[callIndex].currentVertex);
88+
expect(params.previousVertex).toEqual(leaveVertexParamsMap[callIndex].previousVertex);
89+
}
5990
});
6091

6192
it('should allow to create custom vertex visiting logic', () => {
@@ -100,15 +131,44 @@ describe('breadthFirstSearch', () => {
100131
breadthFirstSearch(graph, vertexA, {
101132
enterVertex: enterVertexCallback,
102133
leaveVertex: leaveVertexCallback,
103-
allowTraversal: (vertex, neighbor) => {
104-
return !(vertex === vertexA && neighbor === vertexB);
134+
allowTraversal: ({ currentVertex, nextVertex }) => {
135+
return !(currentVertex === vertexA && nextVertex === vertexB);
105136
},
106137
});
107138

108139
expect(enterVertexCallback).toHaveBeenCalledTimes(7);
109140
expect(leaveVertexCallback).toHaveBeenCalledTimes(7);
110141

111-
expect(enterVertexCallback.mock.calls.toString()).toBe('A,D,E,H,F,D,H');
112-
expect(leaveVertexCallback.mock.calls.toString()).toBe('A,D,E,H,F,D,H');
142+
const enterVertexParamsMap = [
143+
{ currentVertex: vertexA, previousVertex: null },
144+
{ currentVertex: vertexD, previousVertex: vertexA },
145+
{ currentVertex: vertexE, previousVertex: vertexD },
146+
{ currentVertex: vertexH, previousVertex: vertexE },
147+
{ currentVertex: vertexF, previousVertex: vertexH },
148+
{ currentVertex: vertexD, previousVertex: vertexF },
149+
{ currentVertex: vertexH, previousVertex: vertexD },
150+
];
151+
152+
for (let callIndex = 0; callIndex < 7; callIndex += 1) {
153+
const params = enterVertexCallback.mock.calls[callIndex][0];
154+
expect(params.currentVertex).toEqual(enterVertexParamsMap[callIndex].currentVertex);
155+
expect(params.previousVertex).toEqual(enterVertexParamsMap[callIndex].previousVertex);
156+
}
157+
158+
const leaveVertexParamsMap = [
159+
{ currentVertex: vertexA, previousVertex: null },
160+
{ currentVertex: vertexD, previousVertex: vertexA },
161+
{ currentVertex: vertexE, previousVertex: vertexD },
162+
{ currentVertex: vertexH, previousVertex: vertexE },
163+
{ currentVertex: vertexF, previousVertex: vertexH },
164+
{ currentVertex: vertexD, previousVertex: vertexF },
165+
{ currentVertex: vertexH, previousVertex: vertexD },
166+
];
167+
168+
for (let callIndex = 0; callIndex < 7; callIndex += 1) {
169+
const params = leaveVertexCallback.mock.calls[callIndex][0];
170+
expect(params.currentVertex).toEqual(leaveVertexParamsMap[callIndex].currentVertex);
171+
expect(params.previousVertex).toEqual(leaveVertexParamsMap[callIndex].previousVertex);
172+
}
113173
});
114174
});

‎src/algorithms/graph/breadth-first-search/breadthFirstSearch.js

+19-11
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ import Queue from '../../../data-structures/queue/Queue';
22

33
/**
44
* @typedef {Object} Callbacks
5-
* @property {function(vertex: GraphVertex, neighbor: GraphVertex): boolean} allowTraversal -
5+
*
6+
* @property {function(vertices: Object): boolean} [allowTraversal] -
67
* Determines whether DFS should traverse from the vertex to its neighbor
78
* (along the edge). By default prohibits visiting the same vertex again.
8-
* @property {function(vertex: GraphVertex)} enterVertex - Called when DFS enters the vertex.
9-
* @property {function(vertex: GraphVertex)} leaveVertex - Called when DFS leaves the vertex.
9+
*
10+
* @property {function(vertices: Object)} [enterVertex] - Called when BFS enters the vertex.
11+
*
12+
* @property {function(vertices: Object)} [leaveVertex] - Called when BFS leaves the vertex.
1013
*/
1114

1215
/**
@@ -21,9 +24,9 @@ function initCallbacks(callbacks = {}) {
2124
const allowTraversalCallback = (
2225
() => {
2326
const seen = {};
24-
return (vertex, neighbor) => {
25-
if (!seen[neighbor.getKey()]) {
26-
seen[neighbor.getKey()] = true;
27+
return ({ nextVertex }) => {
28+
if (!seen[nextVertex.getKey()]) {
29+
seen[nextVertex.getKey()] = true;
2730
return true;
2831
}
2932
return false;
@@ -50,18 +53,23 @@ export default function breadthFirstSearch(graph, startVertex, originalCallbacks
5053
// Do initial queue setup.
5154
vertexQueue.enqueue(startVertex);
5255

56+
let previousVertex = null;
57+
5358
// Traverse all vertices from the queue.
5459
while (!vertexQueue.isEmpty()) {
5560
const currentVertex = vertexQueue.dequeue();
56-
callbacks.enterVertex(currentVertex);
61+
callbacks.enterVertex({ currentVertex, previousVertex });
5762

5863
// Add all neighbors to the queue for future traversals.
59-
graph.getNeighbors(currentVertex).forEach((neighbor) => {
60-
if (callbacks.allowTraversal(currentVertex, neighbor)) {
61-
vertexQueue.enqueue(neighbor);
64+
graph.getNeighbors(currentVertex).forEach((nextVertex) => {
65+
if (callbacks.allowTraversal({ previousVertex, currentVertex, nextVertex })) {
66+
vertexQueue.enqueue(nextVertex);
6267
}
6368
});
6469

65-
callbacks.leaveVertex(currentVertex);
70+
callbacks.leaveVertex({ currentVertex, previousVertex });
71+
72+
// Memorize current vertex before next loop.
73+
previousVertex = currentVertex;
6674
}
6775
}

‎src/algorithms/graph/depth-first-search/__test__/depthFirstSearch.test.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ describe('depthFirstSearch', () => {
6767
expect(params.previousVertex).toEqual(enterVertexParamsMap[callIndex].previousVertex);
6868
}
6969

70-
const leavingVertexParamsMap = [
70+
const leaveVertexParamsMap = [
7171
{ currentVertex: vertexG, previousVertex: vertexC },
7272
{ currentVertex: vertexC, previousVertex: vertexB },
7373
{ currentVertex: vertexB, previousVertex: vertexA },
@@ -79,8 +79,8 @@ describe('depthFirstSearch', () => {
7979

8080
for (let callIndex = 0; callIndex < graph.getAllVertices().length; callIndex += 1) {
8181
const params = leaveVertexCallback.mock.calls[callIndex][0];
82-
expect(params.currentVertex).toEqual(leavingVertexParamsMap[callIndex].currentVertex);
83-
expect(params.previousVertex).toEqual(leavingVertexParamsMap[callIndex].previousVertex);
82+
expect(params.currentVertex).toEqual(leaveVertexParamsMap[callIndex].currentVertex);
83+
expect(params.previousVertex).toEqual(leaveVertexParamsMap[callIndex].previousVertex);
8484
}
8585
});
8686

@@ -146,7 +146,7 @@ describe('depthFirstSearch', () => {
146146
expect(params.previousVertex).toEqual(enterVertexParamsMap[callIndex].previousVertex);
147147
}
148148

149-
const leavingVertexParamsMap = [
149+
const leaveVertexParamsMap = [
150150
{ currentVertex: vertexG, previousVertex: vertexD },
151151
{ currentVertex: vertexD, previousVertex: vertexA },
152152
{ currentVertex: vertexG, previousVertex: vertexD },
@@ -158,8 +158,8 @@ describe('depthFirstSearch', () => {
158158

159159
for (let callIndex = 0; callIndex < graph.getAllVertices().length; callIndex += 1) {
160160
const params = leaveVertexCallback.mock.calls[callIndex][0];
161-
expect(params.currentVertex).toEqual(leavingVertexParamsMap[callIndex].currentVertex);
162-
expect(params.previousVertex).toEqual(leavingVertexParamsMap[callIndex].previousVertex);
161+
expect(params.currentVertex).toEqual(leaveVertexParamsMap[callIndex].currentVertex);
162+
expect(params.previousVertex).toEqual(leaveVertexParamsMap[callIndex].previousVertex);
163163
}
164164
});
165165
});

0 commit comments

Comments
 (0)
Please sign in to comment.