From 35d1efa4bfd7010107f9833684d81ec43fe38b45 Mon Sep 17 00:00:00 2001 From: Pedro Caminha Date: Wed, 16 Oct 2019 10:10:05 -0300 Subject: [PATCH] adding BFS --- BFS.js | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 BFS.js diff --git a/BFS.js b/BFS.js new file mode 100644 index 00000000..bdabcce7 --- /dev/null +++ b/BFS.js @@ -0,0 +1,174 @@ +import Graph from '../../../../data-structures/Graph/Graph'; +import GraphVertex from '../../../../data-structures/Graph/GraphVertex'; +import GraphEdge from '../../../../data-structures/Graph/GraphEdge'; +import breadthFirstSearch from '../breadthFirstSearch'; + +describe('breadthFirstSearch', () => { + it('should perform BFS operation on Graph', () => { + const Graph = new Graph(true); + + const vertexA = new GraphVertex('A'); + const vertexB = new GraphVertex('B'); + const vertexC = new GraphVertex('C'); + const vertexD = new GraphVertex('D'); + const vertexE = new GraphVertex('E'); + const vertexF = new GraphVertex('F'); + const vertexG = new GraphVertex('G'); + const vertexH = new GraphVertex('H'); + + const edgeAB = new GraphEdge(vertexA, vertexB); + const edgeBC = new GraphEdge(vertexB, vertexC); + const edgeCG = new GraphEdge(vertexC, vertexG); + const edgeAD = new GraphEdge(vertexA, vertexD); + const edgeAE = new GraphEdge(vertexA, vertexE); + const edgeEF = new GraphEdge(vertexE, vertexF); + const edgeFD = new GraphEdge(vertexF, vertexD); + const edgeDH = new GraphEdge(vertexD, vertexH); + const edgeGH = new GraphEdge(vertexG, vertexH); + + Graph + .addEdge(edgeAB) + .addEdge(edgeBC) + .addEdge(edgeCG) + .addEdge(edgeAD) + .addEdge(edgeAE) + .addEdge(edgeEF) + .addEdge(edgeFD) + .addEdge(edgeDH) + .addEdge(edgeGH); + + expect(Graph.toString()).toBe('A,B,C,G,D,E,F,H'); + + const enterVertexCallback = jest.fn(); + const leaveVertexCallback = jest.fn(); + + // Traverse Graphs without callbacks first. + breadthFirstSearch(Graph, vertexA); + + // Traverse Graph with enterVertex and leaveVertex callbacks. + breadthFirstSearch(Graph, vertexA, { + enterVertex: enterVertexCallback, + leaveVertex: leaveVertexCallback, + }); + + expect(enterVertexCallback).toHaveBeenCalledTimes(8); + expect(leaveVertexCallback).toHaveBeenCalledTimes(8); + + const enterVertexParamsMap = [ + { currentVertex: vertexA, previousVertex: null }, + { currentVertex: vertexB, previousVertex: vertexA }, + { currentVertex: vertexD, previousVertex: vertexB }, + { currentVertex: vertexE, previousVertex: vertexD }, + { currentVertex: vertexC, previousVertex: vertexE }, + { currentVertex: vertexH, previousVertex: vertexC }, + { currentVertex: vertexF, previousVertex: vertexH }, + { currentVertex: vertexG, previousVertex: vertexF }, + ]; + + for (let callIndex = 0; callIndex < Graph.getAllVertices().length; callIndex += 1) { + const params = enterVertexCallback.mock.calls[callIndex][0]; + expect(params.currentVertex).toEqual(enterVertexParamsMap[callIndex].currentVertex); + expect(params.previousVertex).toEqual(enterVertexParamsMap[callIndex].previousVertex); + } + + const leaveVertexParamsMap = [ + { currentVertex: vertexA, previousVertex: null }, + { currentVertex: vertexB, previousVertex: vertexA }, + { currentVertex: vertexD, previousVertex: vertexB }, + { currentVertex: vertexE, previousVertex: vertexD }, + { currentVertex: vertexC, previousVertex: vertexE }, + { currentVertex: vertexH, previousVertex: vertexC }, + { currentVertex: vertexF, previousVertex: vertexH }, + { currentVertex: vertexG, previousVertex: vertexF }, + ]; + + for (let callIndex = 0; callIndex < Graph.getAllVertices().length; callIndex += 1) { + const params = leaveVertexCallback.mock.calls[callIndex][0]; + expect(params.currentVertex).toEqual(leaveVertexParamsMap[callIndex].currentVertex); + expect(params.previousVertex).toEqual(leaveVertexParamsMap[callIndex].previousVertex); + } + }); + + it('should allow to create custom vertex visiting logic', () => { + const Graph = new Graph(true); + + const vertexA = new GraphVertex('A'); + const vertexB = new GraphVertex('B'); + const vertexC = new GraphVertex('C'); + const vertexD = new GraphVertex('D'); + const vertexE = new GraphVertex('E'); + const vertexF = new GraphVertex('F'); + const vertexG = new GraphVertex('G'); + const vertexH = new GraphVertex('H'); + + const edgeAB = new GraphEdge(vertexA, vertexB); + const edgeBC = new GraphEdge(vertexB, vertexC); + const edgeCG = new GraphEdge(vertexC, vertexG); + const edgeAD = new GraphEdge(vertexA, vertexD); + const edgeAE = new GraphEdge(vertexA, vertexE); + const edgeEF = new GraphEdge(vertexE, vertexF); + const edgeFD = new GraphEdge(vertexF, vertexD); + const edgeDH = new GraphEdge(vertexD, vertexH); + const edgeGH = new GraphEdge(vertexG, vertexH); + + Graph + .addEdge(edgeAB) + .addEdge(edgeBC) + .addEdge(edgeCG) + .addEdge(edgeAD) + .addEdge(edgeAE) + .addEdge(edgeEF) + .addEdge(edgeFD) + .addEdge(edgeDH) + .addEdge(edgeGH); + + expect(Graph.toString()).toBe('A,B,C,G,D,E,F,H'); + + const enterVertexCallback = jest.fn(); + const leaveVertexCallback = jest.fn(); + + // Traverse Graph with enterVertex and leaveVertex callbacks. + breadthFirstSearch(Graph, vertexA, { + enterVertex: enterVertexCallback, + leaveVertex: leaveVertexCallback, + allowTraversal: ({ currentVertex, nextVertex }) => { + return !(currentVertex === vertexA && nextVertex === vertexB); + }, + }); + + expect(enterVertexCallback).toHaveBeenCalledTimes(7); + expect(leaveVertexCallback).toHaveBeenCalledTimes(7); + + const enterVertexParamsMap = [ + { currentVertex: vertexA, previousVertex: null }, + { currentVertex: vertexD, previousVertex: vertexA }, + { currentVertex: vertexE, previousVertex: vertexD }, + { currentVertex: vertexH, previousVertex: vertexE }, + { currentVertex: vertexF, previousVertex: vertexH }, + { currentVertex: vertexD, previousVertex: vertexF }, + { currentVertex: vertexH, previousVertex: vertexD }, + ]; + + for (let callIndex = 0; callIndex < 7; callIndex += 1) { + const params = enterVertexCallback.mock.calls[callIndex][0]; + expect(params.currentVertex).toEqual(enterVertexParamsMap[callIndex].currentVertex); + expect(params.previousVertex).toEqual(enterVertexParamsMap[callIndex].previousVertex); + } + + const leaveVertexParamsMap = [ + { currentVertex: vertexA, previousVertex: null }, + { currentVertex: vertexD, previousVertex: vertexA }, + { currentVertex: vertexE, previousVertex: vertexD }, + { currentVertex: vertexH, previousVertex: vertexE }, + { currentVertex: vertexF, previousVertex: vertexH }, + { currentVertex: vertexD, previousVertex: vertexF }, + { currentVertex: vertexH, previousVertex: vertexD }, + ]; + + for (let callIndex = 0; callIndex < 7; callIndex += 1) { + const params = leaveVertexCallback.mock.calls[callIndex][0]; + expect(params.currentVertex).toEqual(leaveVertexParamsMap[callIndex].currentVertex); + expect(params.previousVertex).toEqual(leaveVertexParamsMap[callIndex].previousVertex); + } + }); +}); \ No newline at end of file