Content-Length: 7797 | pFad | http://github.com/TheAlgorithms/JavaScript/pull/1774.patch
thub.com
From 258e2bcfb5a156fa5389ab4171044d88b9823d74 Mon Sep 17 00:00:00 2001
From: vedas-dixit
Date: Tue, 15 Apr 2025 17:10:13 +0530
Subject: [PATCH 1/3] Added: Tarjan's SCC algorithm and test cases
---
Graphs/TarjanSCC.js | 109 ++++++++++++++++++++++++++++++++++
Graphs/test/TarjanSCC.test.js | 72 ++++++++++++++++++++++
2 files changed, 181 insertions(+)
create mode 100644 Graphs/TarjanSCC.js
create mode 100644 Graphs/test/TarjanSCC.test.js
diff --git a/Graphs/TarjanSCC.js b/Graphs/TarjanSCC.js
new file mode 100644
index 0000000000..af2f3b3bae
--- /dev/null
+++ b/Graphs/TarjanSCC.js
@@ -0,0 +1,109 @@
+/*
+Tarjan's Algorithm to find all Strongly Connected Components (SCCs) in a directed graph.
+It performs a DFS traversal while keeping track of the discovery and low-link values
+to identify root nodes of SCCs.
+
+Complexity:
+ Time: O(V + E), where V: vertices and E: edges.
+ Space: O(V), for stack | discovery arrays | result.
+
+Reference:
+ https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
+ https://cp-algorithms.com/graph/strongly-connected-components.html
+*/
+
+/**
+ * Finds all strongly connected components in a directed graph using Tarjan's algorithm.
+ *
+ * @param {Object} graph - Directed graph represented as an adjacency list.
+ * @returns {Array>} - List of strongly connected components (each SCC is a list of nodes).
+ * @throws {Error} If the input graph is invalid or empty
+ */
+function TarjanSCC(graph) {
+ // Input validation
+ if (!graph || typeof graph !== 'object' || Array.isArray(graph)) {
+ throw new Error(
+ 'Graph must be a non-null object representing an adjacency list'
+ )
+ }
+
+ if (Object.keys(graph).length === 0) {
+ return []
+ }
+
+ const ids = {} // Discovery time of each node
+ const low = {} // Lowest discovery time reachable from the node
+ const onStack = {} // To track if a node is on the recursion stack
+ const stack = [] // Stack to hold the current path
+ const result = [] // Array to store SCCs
+ let time = 0 // Global timer for discovery time
+
+ /**
+ * Convert node to its proper type (number if numeric string, otherwise string)
+ * @param {string|number} node
+ * @returns {string|number}
+ */
+ function convertNode(node) {
+ return !isNaN(node) && String(Number(node)) === String(node)
+ ? Number(node)
+ : node
+ }
+
+ /**
+ * Recursive DFS function to explore the graph and find SCCs
+ * @param {string|number} node
+ */
+ function dfs(node) {
+ if (!(node in graph)) {
+ throw new Error(`Node ${node} not found in graph`)
+ }
+
+ ids[node] = low[node] = time++
+ stack.push(node)
+ onStack[node] = true
+
+ // Explore all neighbours
+ const neighbors = graph[node]
+ if (!Array.isArray(neighbors)) {
+ throw new Error(`Neighbors of node ${node} must be an array`)
+ }
+
+ for (const neighbor of neighbors) {
+ const convertedNeighbor = convertNode(neighbor)
+ if (!(convertedNeighbor in ids)) {
+ dfs(convertedNeighbor)
+ low[node] = Math.min(low[node], low[convertedNeighbor])
+ } else if (onStack[convertedNeighbor]) {
+ low[node] = Math.min(low[node], ids[convertedNeighbor])
+ }
+ }
+
+ // If the current node is the root of an SCC
+ if (ids[node] === low[node]) {
+ const scc = []
+ let current
+ do {
+ current = stack.pop()
+ onStack[current] = false
+ scc.push(convertNode(current))
+ } while (current !== node)
+ result.push(scc)
+ }
+ }
+
+ // Run DFS for all unvisited nodes
+ try {
+ for (const node in graph) {
+ const convertedNode = convertNode(node)
+ if (!(convertedNode in ids)) {
+ dfs(convertedNode)
+ }
+ }
+ } catch (error) {
+ throw new Error(`Error during graph traversal: ${error.message}`)
+ }
+
+ return result
+}
+
+export { TarjanSCC }
diff --git a/Graphs/test/TarjanSCC.test.js b/Graphs/test/TarjanSCC.test.js
new file mode 100644
index 0000000000..c95b0e6e83
--- /dev/null
+++ b/Graphs/test/TarjanSCC.test.js
@@ -0,0 +1,72 @@
+import { TarjanSCC } from '../TarjanSCC.js'
+
+test('Test Case 1 - Simple graph with two SCCs', () => {
+ const graph = {
+ 0: [1],
+ 1: [2],
+ 2: [0, 3],
+ 3: [4],
+ 4: []
+ }
+ const result = TarjanSCC(graph)
+
+ // Sort the components before comparison since order doesn't matter
+ const expected = [[4], [3], [0, 2, 1]].map((comp) => comp.sort())
+ const actual = result.map((comp) => comp.sort())
+
+ expect(actual).toEqual(expect.arrayContaining(expected))
+})
+
+test('Test Case 2 - All nodes in one SCC', () => {
+ const graph = {
+ A: ['B'],
+ B: ['C'],
+ C: ['A']
+ }
+
+ const result = TarjanSCC(graph)
+
+ // Sort the components before comparison since order doesn't matter
+ const expected = [['A', 'B', 'C']].map((comp) => comp.sort())
+ const actual = result.map((comp) => comp.sort())
+
+ expect(actual).toEqual(expect.arrayContaining(expected))
+})
+
+test('Test Case 3 - Disconnected nodes', () => {
+ const graph = {
+ 1: [],
+ 2: [],
+ 3: []
+ }
+
+ const result = TarjanSCC(graph)
+
+ // Sort the components before comparison since order doesn't matter
+ const expected = [[1], [2], [3]].map((comp) => comp.sort())
+ const actual = result.map((comp) => comp.sort())
+
+ expect(actual).toEqual(expect.arrayContaining(expected))
+})
+
+test('Test Case 4 - Complex Graph', () => {
+ const graph = {
+ 0: [1],
+ 1: [2, 3],
+ 2: [0],
+ 3: [4],
+ 4: [5],
+ 5: [3]
+ }
+
+ const result = TarjanSCC(graph)
+
+ // Sort the components before comparison since order doesn't matter
+ const expected = [
+ [0, 2, 1],
+ [3, 5, 4]
+ ].map((comp) => comp.sort())
+ const actual = result.map((comp) => comp.sort())
+
+ expect(actual).toEqual(expect.arrayContaining(expected))
+})
From 0a18f1084d5bb428745f21d9e243f5af72f7e4fd Mon Sep 17 00:00:00 2001
From: vedas-dixit
Date: Tue, 15 Apr 2025 11:41:40 +0000
Subject: [PATCH 2/3] Updated Documentation in README.md
---
DIRECTORY.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/DIRECTORY.md b/DIRECTORY.md
index 5e8e1f401a..dec9f2c4e8 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -163,6 +163,7 @@
* [NodeNeighbors](Graphs/NodeNeighbors.js)
* [NumberOfIslands](Graphs/NumberOfIslands.js)
* [PrimMST](Graphs/PrimMST.js)
+ * [TarjanSCC](Graphs/TarjanSCC.js)
* **Hashes**
* [MD5](Hashes/MD5.js)
* [SHA1](Hashes/SHA1.js)
From ae3b7fb135f01507162c714788919ada3dcada94 Mon Sep 17 00:00:00 2001
From: vedas-dixit
Date: Tue, 15 Apr 2025 17:33:12 +0530
Subject: [PATCH 3/3] Added a few edge test cases
---
Graphs/test/TarjanSCC.test.js | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/Graphs/test/TarjanSCC.test.js b/Graphs/test/TarjanSCC.test.js
index c95b0e6e83..68b05b8b3d 100644
--- a/Graphs/test/TarjanSCC.test.js
+++ b/Graphs/test/TarjanSCC.test.js
@@ -70,3 +70,23 @@ test('Test Case 4 - Complex Graph', () => {
expect(actual).toEqual(expect.arrayContaining(expected))
})
+
+test('Edge Case - Null input should throw error', () => {
+ expect(() => TarjanSCC(null)).toThrow(
+ 'Graph must be a non-null object representing an adjacency list'
+ )
+})
+
+test('Edge Case - Node with non-array neighbors should throw error', () => {
+ const graph = {
+ A: 'not-an-array'
+ }
+ expect(() => TarjanSCC(graph)).toThrow('Neighbors of node A must be an array')
+})
+
+test('Edge Case - Neighbor not in graph should throw error', () => {
+ const graph = {
+ A: ['B']
+ }
+ expect(() => TarjanSCC(graph)).toThrow('Node B not found in graph')
+})
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/TheAlgorithms/JavaScript/pull/1774.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy