/// <summary> /// The graph traversal algorithm. /// </summary> /// <param name="digraph">The directed acyclic graph to traverse.</param> /// <param name="terminalNodeId">// The 'terminal' node ID, i.e. if traversal reaches this node /// then newConn would form a cycle and we stop/terminate traversal.</param> /// <returns></returns> private bool TraverseGraph(DirectedGraph digraph, int terminalNodeId) { int[] srcIdArr = digraph.ConnectionIdArrays._sourceIdArr; int[] tgtIdArr = digraph.ConnectionIdArrays._targetIdArr; // While there are entries on the stack. while (0 != _traversalStack.Count) { // Get the connection index from the top of stack; this is the next connection to be traversed. int currConnIdx = _traversalStack.Peek(); // Notes. // Before we traverse the current connection, update the stack state to point to the next connection to be // traversed, either from the current node or a parent node. I.e. we modify the stack state ready for when // the traversal down into the current connection completes and returns back to the current node. // // This approach results in tail call optimisation and thus will result in a shallower stack on average. It // also has the side effect that we can no longer examine the stack to observe the traversal path at a given // point in time, since some of the path may no longer be on the stack. MoveForward(srcIdArr, tgtIdArr, currConnIdx); // Test if the next traversal child node has already been visited. int childNodeId = tgtIdArr[currConnIdx]; if (_visitedNodeBitmap[childNodeId]) { continue; } // Test if the connection target is the terminal node. if (childNodeId == terminalNodeId) { return(true); } // We're about to traverse into childNodeId, so mark it as visited to prevent re-traversal. _visitedNodeBitmap[childNodeId] = true; // Search for outgoing connections from childNodeId. int connIdx = digraph.GetFirstConnectionIndex(childNodeId); if (connIdx >= 0) { // childNodeId has outgoing connections; push the first connection onto the stack to mark it for traversal. _traversalStack.Push(connIdx); } } // Traversal has completed without visiting the terminal node, therefore the new connection // does not form a cycle in the graph. return(false); }
/// <summary> /// The graph traversal algorithm. /// </summary> /// <param name="connArr">A set of connections that represents the graph to traverse.</param> /// <param name="terminalNodeId">// The 'terminal' node ID, i.e. if traversal reaches this node then newConn would form a cycle and we stop/terminate traversal.</param> /// <returns></returns> private bool TraverseGraph(DirectedConnection[] connArr, int terminalNodeId) { // While there are entries on the stack. while (0 != _traversalStack.Count) { // Get the connection index from the top of stack; this is the next connection to be traversed. int currConnIdx = _traversalStack.Peek(); // Notes. // Before we traverse the current connection, update the stack state to point to the next connection // to be traversed from the current node. I.e. set up the stack state ready for when the traversal down // into the current connection completes and returns back to the current node. // // This is essentially tail call optimisation, and will result in shorter stacks on average and also // has the side effect that we can no longer examine the stack to observe the traversal path at a given // point in time. MoveForward(connArr, currConnIdx); // Test if the next traversal child node has already been visited. int childNodeId = connArr[currConnIdx].TargetId; int childNodeIdx = _nodeIdxByIdMap.Map(childNodeId); if (_visitedNodes[childNodeIdx]) { continue; } // Test if the connection target is the terminal node. if (childNodeId == terminalNodeId) { return(true); } // We're about to traverse into childNodeId, so mark it as visited to prevent re-traversal. _visitedNodes[childNodeIdx] = true; // Search for outgoing connections from childNodeId. // ENHANCEMENT: Eliminate binary search for childNodeId. int connIdx = DirectedConnectionUtils.GetConnectionIndexBySourceNodeId(connArr, childNodeId); if (connIdx >= 0) { // childNodeId has outgoing connections; push the first connection onto the stack to mark it for traversal. _traversalStack.Push(connIdx); } } // Traversal has completed without visiting the terminal node, therefore the new connection // does not form a cycle in the graph. return(false); }