diff --git a/Graphs/test/DepthFirstSearchIterative.test.js b/Graphs/test/DepthFirstSearchIterative.test.js new file mode 100644 index 0000000000..d01c5c7d68 --- /dev/null +++ b/Graphs/test/DepthFirstSearchIterative.test.js @@ -0,0 +1,12 @@ +import { GraphUnweightedUndirected } from '../DepthFirstSearchIterative.js' + +test('DFSIterative finds existing value and rejects non-existing', () => { + const g = new GraphUnweightedUndirected() + g.addEdge(1, 2) + g.addEdge(2, 3) + g.addEdge(2, 4) + g.addEdge(3, 5) + + expect(g.DFSIterative(5, 1)).toBe(true) // path 5->3->2->1 exists + expect(g.DFSIterative(5, 100)).toBe(false) // 100 not in graph +}) diff --git a/Graphs/test/DepthFirstSearchRecursive.test.js b/Graphs/test/DepthFirstSearchRecursive.test.js new file mode 100644 index 0000000000..f473238921 --- /dev/null +++ b/Graphs/test/DepthFirstSearchRecursive.test.js @@ -0,0 +1,12 @@ +import { GraphUnweightedUndirected } from '../DepthFirstSearchRecursive.js' + +test('DFSRecursive finds existing value and rejects non-existing', () => { + const g = new GraphUnweightedUndirected() + g.addEdge(1, 2) + g.addEdge(2, 3) + g.addEdge(2, 4) + g.addEdge(3, 5) + + expect(g.DFSRecursive(5, 1)).toBe(true) // path 5->3->2->1 exists + expect(g.DFSRecursive(5, 100)).toBe(false) // 100 not in graph +}) diff --git a/Graphs/test/Dijkstra.test.js b/Graphs/test/Dijkstra.test.js new file mode 100644 index 0000000000..44a0afe6b6 --- /dev/null +++ b/Graphs/test/Dijkstra.test.js @@ -0,0 +1,33 @@ +import { createGraph, djikstra } from '../Dijkstra.js' + +// Undirected weighted graph +// 0 --2-- 1 --3-- 3 --4-- 4 +// | | \ +// 5| 1 1 +// | | +// 2 ---- 2 --------- +// From source 0, shortest distances should be: +// 0:0, 1:2, 2:3 (0->1->2), 3:4 (0->1->2->3), 4:8 (0->1->2->3->4) + +test('Dijkstra shortest distances from source 0', () => { + const V = 5 + const E = [ + [0, 1, 2], + [0, 2, 5], + [1, 2, 1], + [1, 3, 3], + [2, 3, 1], + [3, 4, 4] + ] + + const graph = createGraph(V, E) + const dist = djikstra(graph, V, 0) + + const expected = [0, 2, 3, 4, 8] + for (let i = 0; i < V; i++) { + expect(dist[i][0]).toBe(expected[i]) + } + + // parent of source should be -1 by implementation + expect(dist[0][1]).toBe(-1) +}) diff --git a/Graphs/test/DijkstraSmallestPath.test.js b/Graphs/test/DijkstraSmallestPath.test.js new file mode 100644 index 0000000000..11004b297a --- /dev/null +++ b/Graphs/test/DijkstraSmallestPath.test.js @@ -0,0 +1,30 @@ +import { solve } from '../DijkstraSmallestPath.js' + +// Graph represented as adjacency object where graph[u][v] = weight +// Example graph: +// A --1-- B --2-- C +// \3 \ +// \ 5 +// \--4-- D ----- +// Shortest distances from A: A:0, B:1, C:3 (A->B->C), D:4 (A->D) + +test('DijkstraSmallestPath returns shortest distances and paths', () => { + const graph = { + A: { B: 1, D: 4 }, + B: { A: 1, C: 2 }, + C: { B: 2, D: 5 }, + D: { A: 4, C: 5 } + } + + const res = solve(graph, 'A') + + expect(res.A.dist).toBe(0) + expect(res.B.dist).toBe(1) + expect(res.C.dist).toBe(3) + expect(res.D.dist).toBe(4) + + // Path arrays exclude the source by implementation (solutions[s] = []) + expect([...res.B]).toEqual(['B']) + expect([...res.C]).toEqual(['B', 'C']) + expect([...res.D]).toEqual(['D']) +}) diff --git a/Graphs/test/FloydWarshall.test.js b/Graphs/test/FloydWarshall.test.js new file mode 100644 index 0000000000..f2b2b618c4 --- /dev/null +++ b/Graphs/test/FloydWarshall.test.js @@ -0,0 +1,19 @@ +import { FloydWarshall } from '../FloydWarshall.js' + +test('FloydWarshall computes all-pairs shortest paths', () => { + const INF = Infinity + const dist = [ + [0, 1, 2, INF], + [1, 0, INF, INF], + [2, INF, 0, 1], + [INF, INF, 1, 0] + ] + + const out = FloydWarshall(dist) + expect(out).toEqual([ + [0, 1, 2, 3], + [1, 0, 3, 4], + [2, 3, 0, 1], + [3, 4, 1, 0] + ]) +}) diff --git a/Graphs/test/KruskalMST.test.js b/Graphs/test/KruskalMST.test.js new file mode 100644 index 0000000000..8211fcaba5 --- /dev/null +++ b/Graphs/test/KruskalMST.test.js @@ -0,0 +1,55 @@ +import { GraphWeightedUndirectedAdjacencyList } from '../KruskalMST.js' + +function totalWeight(graph) { + // connections: { u: { v: w, ... }, ... } + let sum = 0 + const seen = new Set() + for (const u of Object.keys(graph.connections)) { + for (const v of Object.keys(graph.connections[u])) { + const key = u < v ? `${u}-${v}` : `${v}-${u}` + if (!seen.has(key)) { + seen.add(key) + sum += graph.connections[u][v] + } + } + } + return sum +} + +test('KruskalMST builds a minimum spanning tree', () => { + const g = new GraphWeightedUndirectedAdjacencyList() + // Graph: + // 1-2(1), 2-3(2), 3-4(1), 3-5(100), 4-5(5) + g.addEdge('1', '2', 1) + g.addEdge('2', '3', 2) + g.addEdge('3', '4', 1) + g.addEdge('3', '5', 100) // heavy edge to be excluded + g.addEdge('4', '5', 5) + + const mst = g.KruskalMST() + + // MST should have nodes: 1,2,3,4,5 + expect(Object.keys(mst.connections).sort()).toEqual(['1', '2', '3', '4', '5']) + + // It should have exactly nodes-1 = 4 edges + let edgeCount = 0 + const seen = new Set() + for (const u of Object.keys(mst.connections)) { + for (const v of Object.keys(mst.connections[u])) { + const key = u < v ? `${u}-${v}` : `${v}-${u}` + if (!seen.has(key)) { + seen.add(key) + edgeCount++ + } + } + } + expect(edgeCount).toBe(4) + + // Total weight should be 1 (1-2) + 2 (2-3) + 1 (3-4) + 5 (4-5) = 9 + // (Edge 3-5 with weight 100 must not be selected) + expect(totalWeight(mst)).toBe(9) + + // Ensure excluded heavy edge is not present + expect(mst.connections['3']['5']).toBeUndefined() + expect(mst.connections['5']['3']).toBeUndefined() +})