| | 1 | | namespace MoreStructures.RecImmTrees.Visitor; |
| | 2 | |
|
| | 3 | | /// <inheritdoc cref="DepthFirstTraversal{TEdge, TNode}" path="//*[not(self::summary or self::remarks)]"/> |
| | 4 | | /// <summary> |
| | 5 | | /// A lazy, fully-iterative, depth-first <see cref="IVisitStrategy{TNode, TVisitContext}"/> implementation, i.e. a |
| | 6 | | /// traversing strategy which goes in depth as far as possible along each path of the tree, only backtracking when a |
| | 7 | | /// leaf is reached. |
| | 8 | | /// </summary> |
| | 9 | | /// <remarks> |
| | 10 | | /// <inheritdoc cref="DepthFirstTraversal{TEdge, TNode}" path="/remarks"/> |
| | 11 | | /// <para id="advantages"> |
| | 12 | | /// ADVANTAGES AND DISADVANTAGES |
| | 13 | | /// <br/> |
| | 14 | | /// <inheritdoc cref="DocFragments" path="/remarks/para[@id='fully-iterative-advantages']"/> |
| | 15 | | /// </para> |
| | 16 | | /// </remarks> |
| | 17 | | public class FullyIterativeDepthFirstTraversal<TEdge, TNode> |
| | 18 | | : DepthFirstTraversal<TEdge, TNode> |
| | 19 | | where TEdge : IRecImmDictIndexedTreeEdge<TEdge, TNode> |
| | 20 | | where TNode : IRecImmDictIndexedTreeNode<TEdge, TNode> |
| | 21 | | { |
| 80732 | 22 | | private record struct StackFrame(TreeTraversalVisit<TEdge, TNode> Visit, bool ChildrenStacked); |
| | 23 | |
|
| | 24 | | /// <inheritdoc |
| | 25 | | /// cref="TreeTraversal{TEdge, TNode}.Visit(TNode)" |
| | 26 | | /// path="//*[not(self::summary or self::remarks)]"/> |
| | 27 | | /// <summary> |
| | 28 | | /// <b>Lazily and iteratively</b> visits the structure of the provided <paramref name= "node" />, returning the |
| | 29 | | /// sequence of <see cref="IRecImmDictIndexedTreeNode{TEdge, TNode}"/> of the structure, in depth-first order. |
| | 30 | | /// </summary> |
| | 31 | | /// <remarks> |
| | 32 | | /// <inheritdoc cref="FullyIterativeDepthFirstTraversal{TEdge, TNode}" path="/remarks"/> |
| | 33 | | /// <para id = "algo" > |
| | 34 | | /// ALGORITHM |
| | 35 | | /// <br/> |
| | 36 | | /// The algorithm uses a <see cref="Stack{T}"/>: |
| | 37 | | /// <br/> |
| | 38 | | /// - At the beginning the stack contains only a frame with the root node, no parent node nor incoming edge and |
| | 39 | | /// with a <see cref="bool"/> indicating the children for this node haven't been added to the stack yet; |
| | 40 | | /// <br/> |
| | 41 | | /// - Then each frame at the top of the stack is popped out and processed, until the stack is empty. |
| | 42 | | /// <br/> |
| | 43 | | /// - If the node being processed has the "children stacked" flag not set, all children are stacked up. The |
| | 44 | | /// node itself is also stacked up, again, this time with the "children stacked" flag set. |
| | 45 | | /// - If the node being processed has the "children stacked" flag set, or is a leaf, it's yielded to the |
| | 46 | | /// output sequence, so that the client code implementing the visitor can lazily process the nodes. |
| | 47 | | /// </para> |
| | 48 | | /// <para id="complexity"> |
| | 49 | | /// COMPLEXITY |
| | 50 | | /// <br/> |
| | 51 | | /// - Each of the n nodes and n - 1 edges of the tree is visited at most twice: the first time with the |
| | 52 | | /// "children stacked" flag unset and a second time with the flag set. Leafs are only visited once, since |
| | 53 | | /// they have no children and don't have to wait for their children to be visited. |
| | 54 | | /// <br/> |
| | 55 | | /// - <see cref="TreeTraversal{TEdge, TNode}.ChildrenSorter"/> can also increase time and space complexity, |
| | 56 | | /// especially if it perform an actual sorting of nodes. For example, if the sorter takes n * log(n) time |
| | 57 | | /// <br/> |
| | 58 | | /// - The <see cref="IEnumerable{T}"/> emitted by <see cref="TreeTraversal{TEdge, TNode}.ChildrenSorter"/> is |
| | 59 | | /// reversed to be pushed onto the stack in the right order, and that takes additional O(n - 1) total space, |
| | 60 | | /// since there are n - 1 edges, which are 1-to-1 with nodes in the tree. |
| | 61 | | /// <br/> |
| | 62 | | /// - Each frame processing of a node with the "children stacked" flag set takes constant time (e.g.to check |
| | 63 | | /// traversal order) and space (e.g. to extract parent node, incoming edge and node itself from the frame and |
| | 64 | | /// to build a <see cref="TreeTraversalVisit{TEdge, TNode}"/> object for the visit). |
| | 65 | | /// <br/> |
| | 66 | | /// - Time Complexity is O(n * Ts) in total, where Ts is the amortized Time Complexity of |
| | 67 | | /// <see cref="TreeTraversal{TEdge, TNode}.ChildrenSorter"/> per edge/node. Taking into account the visit of |
| | 68 | | /// each emitted node, Time Complexity is O(n * Ts * Tv), where Tv is the Time Complexity of the visitor per |
| | 69 | | /// node. |
| | 70 | | /// <br/> |
| | 71 | | /// - Space Complexity is O(n * Ss) in total, where Ss is the amortized Space Complexity of |
| | 72 | | /// <see cref="TreeTraversal{TEdge, TNode}.ChildrenSorter"/> per edge/node. Taking into account the visit of |
| | 73 | | /// each emitted node, Space Complexity is O(n * (Ss + Sv)), where Sv is the Space Complexity of the visitor |
| | 74 | | /// per node. |
| | 75 | | /// </para> |
| | 76 | | /// </remarks> |
| | 77 | | public override IEnumerable<TreeTraversalVisit<TEdge, TNode>> Visit(TNode node) |
| 15 | 78 | | { |
| 15 | 79 | | var stack = new Stack<StackFrame> { }; |
| 15 | 80 | | stack.Push(new(new(node, default, default, 0), false)); |
| 20196 | 81 | | while (stack.Count > 0) |
| 20183 | 82 | | { |
| 20183 | 83 | | if (ProcessStack(stack) is TreeTraversalVisit<TEdge, TNode> visit) |
| 10126 | 84 | | yield return visit; |
| 20181 | 85 | | } |
| 13 | 86 | | } |
| | 87 | |
|
| | 88 | | private TreeTraversalVisit<TEdge, TNode>? ProcessStack(Stack<StackFrame> stack) |
| 20183 | 89 | | { |
| 20183 | 90 | | var (visit, childrenStacked) = stack.Pop(); |
| 20183 | 91 | | var (node, _, _, level) = visit; |
| | 92 | |
|
| 20183 | 93 | | if (node.IsLeaf()) |
| 72 | 94 | | { |
| 72 | 95 | | return TraversalOrder switch |
| 72 | 96 | | { |
| 71 | 97 | | TreeTraversalOrder.ParentFirst or TreeTraversalOrder.ChildrenFirst => visit, |
| 1 | 98 | | _ => throw new NotSupportedException( |
| 1 | 99 | | $"{nameof(TreeTraversalOrder)} {TraversalOrder} is not supported."), |
| 72 | 100 | | }; |
| | 101 | | } |
| | 102 | |
|
| 20111 | 103 | | if (!childrenStacked) |
| 10056 | 104 | | { |
| 10056 | 105 | | switch (TraversalOrder) |
| | 106 | | { |
| | 107 | | case TreeTraversalOrder.ParentFirst: |
| 50296 | 108 | | foreach (var child in ChildrenSorter(visit).Reverse()) |
| 10085 | 109 | | stack.Push(new(new(child.Value, node, child.Key, level + 1), false)); |
| 10042 | 110 | | stack.Push(new(visit, true)); |
| | 111 | |
|
| 10042 | 112 | | break; |
| | 113 | |
|
| | 114 | | case TreeTraversalOrder.ChildrenFirst: |
| 13 | 115 | | stack.Push(new(visit, true)); |
| 95 | 116 | | foreach (var child in ChildrenSorter(visit).Reverse()) |
| 28 | 117 | | stack.Push(new(new(child.Value, node, child.Key, level + 1), false)); |
| | 118 | |
|
| 13 | 119 | | break; |
| | 120 | |
|
| | 121 | | default: |
| 1 | 122 | | throw new NotSupportedException( |
| 1 | 123 | | $"{nameof(TreeTraversalOrder)} {TraversalOrder} is not supported."); |
| | 124 | | } |
| | 125 | |
|
| 10055 | 126 | | return null; |
| | 127 | | } |
| | 128 | |
|
| 10055 | 129 | | return visit; |
| 20181 | 130 | | } |
| | 131 | | } |