Example #1
0
        /// <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);
        }