/// <summary> /// Enumerates a tree using the pre-order traversal method. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child nodes. /// </param> /// <param name="node">The root node of the tree that is to be traversed.</param> /// <param name="excludeSubtreePredicate"> /// A <see cref="System.Func<T, Int32, Boolean>"/> that determines if the current node /// that is being evaluated (and all of its descendants) should be included in the /// traversal. This allows for short-circuiting of the pre-order traversal by excluding /// particular subtrees from the traversal. The first argument is the current node being /// evaluated and the second argument is the depth of the current node relative to the /// original node that the traversal began on. /// </param> /// <param name="excludeOption"> /// Used in conjunction with the <paramref name="excludeSubtreePredicate"/>. Determines /// if the entire subtree should be excluded or just its children. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains all the /// nodes in the tree ordered based on a pre-order traversal. /// </returns> public static IEnumerable <T> PreOrderTraversal <T>( this ITreeWalker <T> walker, T node, Func <T, int, bool> excludeSubtreePredicate, ExcludeOption excludeOption) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // Create a stack to keep track of the branches being traversed. Stack <IEnumerator <T> > enumerators = new Stack <IEnumerator <T> >(); enumerators.Push(Enumerable.Repeat(node, 1).GetEnumerator()); // Loop as long as we have an enumerator on the stack. When we have popped the last // one off the stack the traversal is complete. while (enumerators.Count > 0) { if (enumerators.Peek().MoveNext()) { node = enumerators.Peek().Current; // If the 'excludeSubtreePredicate' is not null and evaluates to true then // yield the current node if 'excludeOption' is set to exclude the children. // Otherwise, do not yield anything. if (excludeSubtreePredicate != null && excludeSubtreePredicate.Invoke(node, enumerators.Count - 1)) { if (excludeOption == ExcludeOption.ExcludeDescendants) { yield return(node); } } else { // Yield the current node then push it and its children onto the // stack. yield return(node); enumerators.Push(walker.GetChildren(node).GetEnumerator()); } } else { // Pop the enumerator and dispose of it. enumerators.Pop().Dispose(); } } }
public IEnumerable <T> PreOrderTraversal( Func <T, int, bool> excludeSubtreePredicate, ExcludeOption excludeOption) { return (this .TreeWalker .PreOrderTraversal(this.Root, excludeSubtreePredicate, excludeOption)); }
public IEnumerable <T> LevelOrderTraversal( Func <T, int, bool> predicate, ExcludeOption excludeOption) { return (this .TreeWalker .LevelOrderTraversal(this.Root, predicate, excludeOption)); }
public void PreOrderTraversal_SingleNode( ExcludeOption excludeOption, int[] expectedResults) { // Get a valid tree. var tree = Node.Create(0); // Get a valid ITreeWalker. NodeWalker <int> walker = new NodeWalker <int>(); // Assert that the correct sequence is returned. Assert.Equal( expectedResults, walker.PreOrderTraversal(tree, (n, i) => n.Value == 0, excludeOption).Select(x => x.Value)); }
public void PreOrderTraversal_ShortCircuitRootNode( ExcludeOption excludeOption, int[] expectedResults) { // Get a valid tree. var tree = TestTreeFactory.GetSimpleTree(); // Get a valid ITreeWalker. NodeWalker <int> walker = new NodeWalker <int>(); // Assert that the correct sequence is returned. Assert.Equal( expectedResults, walker.PreOrderTraversal(tree, (n, i) => n.Value == 0, excludeOption).Select(x => x.Value)); }
public void LevelOrderTraversal_ShortCircuitOddNumbers( int[] traversalToStartNode, ExcludeOption excludeOption, int[] expectedResults) { // Get a valid tree. var tree = TestTreeFactory.GetSimpleTree(); // Get a valid ITreeWalker. NodeWalker <int> walker = new NodeWalker <int>(); // Get the node to begin traversing from. var startNode = tree; foreach (int i in traversalToStartNode) { startNode = startNode[i]; } // Assert that the correct sequence is returned. Assert.Equal( expectedResults, walker.LevelOrderTraversal(startNode, (n, i) => n.Value % 2 == 1, excludeOption).Select(x => x.Value)); }
/// <summary> /// Enumerates a tree using the post-order traversal method. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child nodes. /// </param> /// <param name="node">The root node of the tree that is to be traversed.</param> /// <param name="excludeSubtreePredicate"> /// A <see cref="System.Func<T, Int32, Boolean>"/> that determines if the current node /// that is being evaluated (and all of its descendants) should be included in the /// traversal. This allows for short-circuiting of the post-order traversal by excluding /// particular subtrees from the traversal. The first argument is the current node being /// evaluated and the second argument is the depth of the current node relative to the /// original node that the traversal began on. /// </param> /// <param name="excludeOption"> /// Used in conjunction with the <paramref name="excludeSubtreePredicate"/>. Determines /// if the entire subtree should be excluded or just its children. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains all the /// nodes in the tree ordered based on a post-order traversal. /// </returns> public static IEnumerable <T> PostOrderTraversal <T>( this ITreeWalker <T> walker, T node, Func <T, int, bool> excludeSubtreePredicate, ExcludeOption excludeOption) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // Create a stack to keep track of the branches being traversed. Stack <IEnumerator <T> > enumerators = new Stack <IEnumerator <T> >(); enumerators.Push(Enumerable.Repeat(node, 1).GetEnumerator()); // Loop as long as we have an enumerator on the stack. When we have popped the last // one off the stack the traversal is complete. while (enumerators.Count > 0) { if (enumerators.Peek().MoveNext()) { node = enumerators.Peek().Current; // If the 'excludeSubtreePredicate' is not null and evaluates to true then // yield the current node if 'excludeOption' is set to exclude the children. // Otherwise, do not yield anything. if (excludeSubtreePredicate != null && excludeSubtreePredicate.Invoke(node, enumerators.Count - 1)) { if (excludeOption == ExcludeOption.ExcludeDescendants) { yield return(node); } } else { // Push the current node's children onto the stack. enumerators.Push(walker.GetChildren(node).GetEnumerator()); } } else { // Pop the enumerator and dispose of it. enumerators.Pop().Dispose(); // Yield the 'Current' value of the enumerator on the top of the stack. if (enumerators.Count > 0) { yield return(enumerators.Peek().Current); } } } //// If the 'excludeSubtreePredicate' is not null and evaluates to true then return //// 'node' or return an empty collection, depending on 'excludeOption'. //if (excludeSubtreePredicate != null && excludeSubtreePredicate.Invoke(node, 0)) //{ // if (excludeOption == ExcludeOption.ExcludeDescendants) // { // yield return node; // } // yield break; //} //else //{ // // Create stacks to keep track of the branches being traversed. // Stack<IEnumerator<T>> enumerators = new Stack<IEnumerator<T>>(); // Stack<T> nodes = new Stack<T>(); // // Push the current node and its children onto the stacks. // nodes.Push(node); // enumerators.Push(walker.GetChildren(node).GetEnumerator()); // // Loop as long as we have a node on the stack. When we have popped the last node // // off the stack the traversal is complete. // while (nodes.Count > 0) // { // // Try and move to the current node's next child. // if (enumerators.Peek().MoveNext()) // { // // If we successfully moved to the next child then set the current node // // to that child. // node = enumerators.Peek().Current; // // If the 'excludeSubtreePredicate' is not null and evaluates to true then // // return yield the current node if 'excludeOption' is set to exclude the // // children. Otherwise, do not yield anything. // if (excludeSubtreePredicate != null && // excludeSubtreePredicate.Invoke(node, nodes.Count)) // { // if (excludeOption == ExcludeOption.ExcludeDescendants) // { // yield return node; // } // } // else // { // // Push the current node and its children onto the stacks. // nodes.Push(node); // enumerators.Push(walker.GetChildren(node).GetEnumerator()); // } // } // else // { // // If the current node does not have any more children then pop it off of // // the 'nodes' stack and pop its children enumerator off the 'enumerators' // // stack. // // Yield the node that was popped off the stack. // enumerators.Pop().Dispose(); // yield return nodes.Pop(); // } // } //} }
/// <summary> /// Enumerates a tree using the level-order traversal method. I.e. it /// returns all nodes in the first level relative to the specified node, /// followed by all nodes in the second level, etc... /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the /// parent and child nodes. /// </param> /// <param name="node"> /// The root node of the tree that is to be traversed. /// </param> /// <param name="excludeSubtreePredicate"> /// A <see cref="System.Func<T, int, bool>"/> that determines if /// the current node that is being evaluated (and all of its descendants) /// should be included in the traversal. This allows for short-circuiting /// of the level-order traversal by excluding particular subtrees from /// the traversal. The first argument is the current node being evaluated /// and the second argument is the depth of the current node relative to /// the original node that the traversal began on. /// </param> /// <param name="excludeOption"> /// Used in conjunction with the <paramref /// name="excludeSubtreePredicate"/>. Determines if the entire subtree /// should be excluded or just its children. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that /// contains all the nodes in the tree ordered based on a level-order /// traversal. /// </returns> public static IEnumerable <T> LevelOrderTraversal <T>( this ITreeWalker <T> walker, T node, Func <T, int, bool> excludeSubtreePredicate, ExcludeOption excludeOption) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // Set the initial depth to 0. int depth = 0; // Set 'node' as the current level. List <IEnumerable <T> > currentLevel = new List <IEnumerable <T> >() { new T[] { node } }; List <IEnumerable <T> > nextLevel = new List <IEnumerable <T> >(); // Track whether the current level has any nodes. This is true // initially since the current level is simply the node that was // passed to this function. bool currentLevelHasNodes = true; // Enumerate 'currentLevel' and yield each node while adding that // node's children to 'nextLevel'. When complete: set 'currentLevel' // equal to 'nextLevel', increment 'depth' and repeat the process. while (currentLevelHasNodes) { // Set current level has nodes to false when we enter the loop. // This will be set to true again if any nodes are concatenated // to 'nextLevel'. currentLevelHasNodes = false; foreach (T currentNode in currentLevel.SelectMany(x => x)) { // If the 'excludeSubtreePredicate' is not null and evaluates // to true then exlucude this node and all of its // descendants, depending on 'excludeOption', from the // traversal result. if (excludeSubtreePredicate != null && excludeSubtreePredicate.Invoke(currentNode, depth)) { if (excludeOption == ExcludeOption.ExcludeDescendants) { yield return(currentNode); } } else { yield return(currentNode); nextLevel.Add(walker.GetChildren(currentNode)); currentLevelHasNodes = true; } } // Swap the lists. I am reusing lists rather than simply creating // a new empty list so that there are fewer objects to be garbage // collected. var temp = currentLevel; currentLevel = nextLevel; nextLevel = temp; nextLevel.Clear(); // Increment the depth counter. depth++; } }