( graph: Graph<N, E, T> | MutableGraph<N, E, T> )
| 3508 | * @since 3.18.0 |
| 3509 | */ |
| 3510 | export const isAcyclic = <N, E, T extends Kind = "directed">( |
| 3511 | graph: Graph<N, E, T> | MutableGraph<N, E, T> |
| 3512 | ): boolean => { |
| 3513 | const impl = graphImpl(graph) |
| 3514 | // Use existing cycle flag if available |
| 3515 | if (Option.isSome(impl.acyclic)) { |
| 3516 | return impl.acyclic.value |
| 3517 | } |
| 3518 | |
| 3519 | if (graph.type === "undirected") { |
| 3520 | const visited = new Set<NodeIndex>() |
| 3521 | |
| 3522 | for (const startNode of impl.nodes.keys()) { |
| 3523 | if (visited.has(startNode)) { |
| 3524 | continue |
| 3525 | } |
| 3526 | |
| 3527 | visited.add(startNode) |
| 3528 | const stack: Array<{ node: NodeIndex; incoming: EdgeIndex | null }> = [{ node: startNode, incoming: null }] |
| 3529 | |
| 3530 | while (stack.length > 0) { |
| 3531 | const { node, incoming } = stack.pop()! |
| 3532 | const adjacencyList = impl.adjacency.get(node) |
| 3533 | if (adjacencyList === undefined) { |
| 3534 | continue |
| 3535 | } |
| 3536 | |
| 3537 | for (const edgeIndex of adjacencyList) { |
| 3538 | if (edgeIndex === incoming) { |
| 3539 | continue |
| 3540 | } |
| 3541 | const edge = impl.edges.get(edgeIndex) |
| 3542 | if (edge === undefined) { |
| 3543 | continue |
| 3544 | } |
| 3545 | const neighbor = getTraversableNeighbor(graph, node, edge) |
| 3546 | if (!visited.has(neighbor)) { |
| 3547 | visited.add(neighbor) |
| 3548 | stack.push({ node: neighbor, incoming: edgeIndex }) |
| 3549 | } else { |
| 3550 | impl.acyclic = Option.some(false) |
| 3551 | return false |
| 3552 | } |
| 3553 | } |
| 3554 | } |
| 3555 | } |
| 3556 | |
| 3557 | impl.acyclic = Option.some(true) |
| 3558 | return true |
| 3559 | } |
| 3560 | |
| 3561 | // Stack-safe DFS cycle detection using iterative approach |
| 3562 | const visited = new Set<NodeIndex>() |
| 3563 | const recursionStack = new Set<NodeIndex>() |
| 3564 | |
| 3565 | // Stack entry: [node, neighbors, neighborIndex, isFirstVisit] |
| 3566 | type DfsStackEntry = [NodeIndex, Array<NodeIndex>, number, boolean] |
| 3567 |
no test coverage detected