| | 1 | | namespace MoreStructures.RecImmTrees.Paths; |
| | 2 | |
|
| | 3 | | /// <summary> |
| | 4 | | /// <inheritdoc/> |
| | 5 | | /// <br/> |
| | 6 | | /// Iterative implementation. |
| | 7 | | /// </summary> |
| | 8 | | /// <remarks> |
| | 9 | | /// <inheritdoc cref="INodeToLeafPathsBuilder" path="/remarks"/> |
| | 10 | | /// <para id="advantages"> |
| | 11 | | /// ADVANTAGES AND DISADVANTAGES |
| | 12 | | /// <br/> |
| | 13 | | /// <inheritdoc cref="DocFragments" path="/remarks/para[@id='fully-iterative-advantages']"/> |
| | 14 | | /// </para> |
| | 15 | | /// <para id="algo"> |
| | 16 | | /// ALGORITHM |
| | 17 | | /// <br/> |
| | 18 | | /// The implementation uses a <see cref="Stack{T}"/>, onto which quadrules are stacked up: |
| | 19 | | /// <br/> |
| | 20 | | /// (incoming edge, node, queue of subpaths of the parent node of node, queue of subpaths of the node itself). |
| | 21 | | /// <br/> |
| | 22 | | /// - A queue of subpaths of the root node is instantiated empty. It will collect the output |
| | 23 | | /// <see cref="TreePath{TEdge, TNode}"/> instances. |
| | 24 | | /// <br/> |
| | 25 | | /// - Children (node and incoming edge) of the root node are pushed onto the stack in reverse order. The queue of |
| | 26 | | /// subpaths of the root is passed to each child. The queue of the child is set to <see langword="null"/>, since |
| | 27 | | /// children of the child haven't been processed yet (i.e. the child queue of subpath hasn't been populated yet). |
| | 28 | | /// <br/> |
| | 29 | | /// - Then the stack processing loop is performed. After every stack item is processed, the queue of subpaths of |
| | 30 | | /// the root is unloaded, emitting <see cref="TreePath{TEdge, TNode}"/> instances from the collected |
| | 31 | | /// <see cref="IEnumerable{T}"/> of <see cref="KeyValuePair{TKey, TValue}"/> of edges and nodes. |
| | 32 | | /// <br/> |
| | 33 | | /// <br/> |
| | 34 | | /// Stack processing iteration: |
| | 35 | | /// <br/> |
| | 36 | | /// - The frame at the top of the stack is popped. |
| | 37 | | /// <br/> |
| | 38 | | /// - If the node is a leaf, the node and its incoming edge are added to the queue of subpaths of the parent node. |
| | 39 | | /// <br/> |
| | 40 | | /// - If the node has children and the queue of subpaths of the node is set, children have been already processed. |
| | 41 | | /// So as many paths are enqueued to the queue of subpaths of the parent node as paths in the queue of subpaths |
| | 42 | | /// of the node. Each subpath of the node is prepended with the node itself. |
| | 43 | | /// <br/> |
| | 44 | | /// - If the node has children and the queue of subpaths of the node is not set, children have not been processed |
| | 45 | | /// yet. So create a new queue for the subpaths of the node and push the node back to the stack, this time with |
| | 46 | | /// the queue of subpaths set. Then push all the children of the node to the stack (in reverse order), passing |
| | 47 | | /// the queue of subpaths of the node as queue of subpaths of the parent, and the queue of subpaths of the child |
| | 48 | | /// not set. |
| | 49 | | /// </para> |
| | 50 | | /// <para id="complexity"> |
| | 51 | | /// COMPLEXITY |
| | 52 | | /// <br/> |
| | 53 | | /// - Each node is pushed to the stack and then processed at most twice (only once for leaves). So the maximum |
| | 54 | | /// number of items in the stack is 2 * n, where n = number of nodes in the tree. |
| | 55 | | /// <br/> |
| | 56 | | /// - The total number of paths from root to leaf is equal to the total number of leaves of the tree, which can |
| | 57 | | /// be at most n (if the root itself is a leaf), n - 1 (if the root is not a leaf and the tree is completely |
| | 58 | | /// flat) or anything down to 1 (if the tree is as unbalanced as it can be). |
| | 59 | | /// <br/> |
| | 60 | | /// - Each stack processing iteration does constant time work: stack item deconstruction, check whether the node is |
| | 61 | | /// a leaf or not, instantiate a queue of subpaths of the node. Children are iterated, however, the total number |
| | 62 | | /// of children iteration over all the nodes of the tree is equal to the number of edges, which is n - 1. |
| | 63 | | /// <br/> |
| | 64 | | /// - For each non-leaf node (and there can be O(n) of those), a queue of subpaths is instantiated and populated. |
| | 65 | | /// Such queue can have O(n) items in it (for a flattish tree), each being a subpath of length O(n). This would |
| | 66 | | /// suggest a cubic complexity. However, there is at most a total of n root-to-leaf paths, each being at most of |
| | 67 | | /// length n, so overall Space Complexity is quadratic. |
| | 68 | | /// <br/> |
| | 69 | | /// - So Time Complexity is O(n) and Space Complexity is O(n^2). |
| | 70 | | /// </para> |
| | 71 | | /// </remarks> |
| | 72 | | public class FullyIterativeNodeToLeafPathsBuilder : INodeToLeafPathsBuilder |
| | 73 | | { |
| | 74 | | private record struct StackFrame<TEdge, TNode>( |
| 5528 | 75 | | TEdge Edge, |
| 5528 | 76 | | TNode Node, |
| 5528 | 77 | | Queue<IEnumerable<KeyValuePair<TEdge, TNode>>> ParentNodeSubpaths, |
| 5528 | 78 | | Queue<IEnumerable<KeyValuePair<TEdge, TNode>>>? NodeSubpaths); |
| | 79 | |
|
| | 80 | | /// <inheritdoc path="//*[not(self::remarks)]"/> |
| | 81 | | /// <inheritdoc cref="FullyIterativeNodeToLeafPathsBuilder" path="/remarks"/> |
| | 82 | | public IEnumerable<TreePath<TEdge, TNode>> GetAllNodeToLeafPaths<TEdge, TNode>(TNode node) |
| | 83 | | where TEdge : IRecImmDictIndexedTreeEdge<TEdge, TNode> |
| | 84 | | where TNode : IRecImmDictIndexedTreeNode<TEdge, TNode> |
| 395 | 85 | | { |
| 395 | 86 | | var stack = new Stack<StackFrame<TEdge, TNode>>(); |
| 395 | 87 | | var rootSubpaths = new Queue<IEnumerable<KeyValuePair<TEdge, TNode>>> { }; |
| | 88 | |
|
| 2855 | 89 | | foreach (var child in node.Children.Reverse()) |
| 835 | 90 | | { |
| 835 | 91 | | stack.Push(new(child.Key, child.Value, rootSubpaths, null)); |
| 835 | 92 | | } |
| | 93 | |
|
| 2704 | 94 | | while (stack.Count > 0) |
| 2610 | 95 | | { |
| 2610 | 96 | | ProcessStack(stack); |
| | 97 | |
|
| 3055 | 98 | | while (rootSubpaths.Count > 0) |
| 746 | 99 | | yield return new TreePath<TEdge, TNode>(rootSubpaths.Dequeue()); |
| 2309 | 100 | | } |
| 94 | 101 | | } |
| | 102 | |
|
| | 103 | | private static void ProcessStack<TEdge, TNode>(Stack<StackFrame<TEdge, TNode>> stack) |
| | 104 | | where TEdge : IRecImmDictIndexedTreeEdge<TEdge, TNode> |
| | 105 | | where TNode : IRecImmDictIndexedTreeNode<TEdge, TNode> |
| 2610 | 106 | | { |
| 2610 | 107 | | var (edge, node, parentNodeSubpaths, nodeSubpaths) = stack.Pop(); |
| | 108 | |
|
| 2610 | 109 | | if (node.IsLeaf()) |
| 826 | 110 | | { |
| 826 | 111 | | parentNodeSubpaths.Enqueue(new [] { KeyValuePair.Create(edge, node) }); |
| 826 | 112 | | return; |
| | 113 | | } |
| | 114 | |
|
| 1784 | 115 | | if (nodeSubpaths != null) |
| 892 | 116 | | { |
| | 117 | | // Children have already been processed |
| 5470 | 118 | | foreach (var nodeSubpath in nodeSubpaths) |
| 1397 | 119 | | parentNodeSubpaths.Enqueue(nodeSubpath.Prepend(KeyValuePair.Create(edge, node))); |
| 892 | 120 | | } |
| | 121 | | else |
| 892 | 122 | | { |
| | 123 | | // Children have not been processed yet |
| 892 | 124 | | nodeSubpaths = new Queue<IEnumerable<KeyValuePair<TEdge, TNode>>> { }; |
| 892 | 125 | | stack.Push(new(edge, node, parentNodeSubpaths, nodeSubpaths)); |
| 5058 | 126 | | foreach (var child in node.Children.Reverse()) |
| 1191 | 127 | | { |
| 1191 | 128 | | stack.Push(new(child.Key, child.Value, nodeSubpaths, null)); |
| 1191 | 129 | | } |
| 892 | 130 | | } |
| 2610 | 131 | | } |
| | 132 | | } |