( graph: Graph<N, E, T> | MutableGraph<N, E, T> )
| 1887 | * @category algorithms |
| 1888 | */ |
| 1889 | export const isAcyclic = <N, E, T extends Kind = "directed">( |
| 1890 | graph: Graph<N, E, T> | MutableGraph<N, E, T> |
| 1891 | ): boolean => { |
| 1892 | // Use existing cycle flag if available |
| 1893 | if (Option.isSome(graph.isAcyclic)) { |
| 1894 | return graph.isAcyclic.value |
| 1895 | } |
| 1896 | |
| 1897 | if (graph.type === "undirected") { |
| 1898 | const visited = new Set<NodeIndex>() |
| 1899 | |
| 1900 | for (const startNode of graph.nodes.keys()) { |
| 1901 | if (visited.has(startNode)) { |
| 1902 | continue |
| 1903 | } |
| 1904 | |
| 1905 | visited.add(startNode) |
| 1906 | const stack: Array<{ node: NodeIndex; parent: NodeIndex | null }> = [{ node: startNode, parent: null }] |
| 1907 | |
| 1908 | while (stack.length > 0) { |
| 1909 | const { node, parent } = stack.pop()! |
| 1910 | const nodeNeighbors = getUndirectedNeighbors(graph as any, node) |
| 1911 | |
| 1912 | for (const neighbor of nodeNeighbors) { |
| 1913 | if (!visited.has(neighbor)) { |
| 1914 | visited.add(neighbor) |
| 1915 | stack.push({ node: neighbor, parent: node }) |
| 1916 | } else if (neighbor !== parent) { |
| 1917 | graph.isAcyclic = Option.some(false) |
| 1918 | return false |
| 1919 | } |
| 1920 | } |
| 1921 | } |
| 1922 | } |
| 1923 | |
| 1924 | graph.isAcyclic = Option.some(true) |
| 1925 | return true |
| 1926 | } |
| 1927 | |
| 1928 | // Stack-safe DFS cycle detection using iterative approach |
| 1929 | const visited = new Set<NodeIndex>() |
| 1930 | const recursionStack = new Set<NodeIndex>() |
| 1931 | |
| 1932 | // Stack entry: [node, neighbors, neighborIndex, isFirstVisit] |
| 1933 | type DfsStackEntry = [NodeIndex, Array<NodeIndex>, number, boolean] |
| 1934 | |
| 1935 | // Get all nodes to handle disconnected components |
| 1936 | for (const startNode of graph.nodes.keys()) { |
| 1937 | if (visited.has(startNode)) { |
| 1938 | continue // Already processed this component |
| 1939 | } |
| 1940 | |
| 1941 | // Iterative DFS with explicit stack |
| 1942 | const stack: Array<DfsStackEntry> = [[startNode, [], 0, true]] |
| 1943 | |
| 1944 | while (stack.length > 0) { |
| 1945 | const [node, neighbors, neighborIndex, isFirstVisit] = stack[stack.length - 1] |
| 1946 |
no test coverage detected