using System; using System.Collections.Generic; using System.Linq; using ICD.Common.Properties; using ICD.Common.Utils.Collections; namespace ICD.Common.Utils { public static class RecursionUtils { /// /// Returns all of the nodes in the tree via breadth-first search. /// /// /// /// /// public static IEnumerable BreadthFirstSearch(T root, Func> getChildren) { if (getChildren == null) throw new ArgumentNullException("getChildren"); Queue process = new Queue(); process.Enqueue(root); while (process.Count > 0) { T current = process.Dequeue(); yield return current; foreach (T child in getChildren(current)) process.Enqueue(child); } } /// /// Returns the shortest path from root to destination via breadth-first search. /// /// /// /// /// /// [CanBeNull] public static IEnumerable BreadthFirstSearchPath(T root, T destination, Func> getChildren) { if (getChildren == null) throw new ArgumentNullException("getChildren"); return BreadthFirstSearchPath(root, destination, getChildren, EqualityComparer.Default); } /// /// Returns the shortest path from root to destination via breadth-first search. /// /// /// /// /// /// /// [CanBeNull] public static IEnumerable BreadthFirstSearchPath(T root, T destination, Func> getChildren, IEqualityComparer comparer) { if (getChildren == null) throw new ArgumentNullException("getChildren"); if (comparer == null) throw new ArgumentNullException("comparer"); Queue queue = new Queue(); queue.Enqueue(root); Dictionary nodeParents = new Dictionary(); while (queue.Count > 0) { T current = queue.Dequeue(); foreach (T node in getChildren(current)) { if (nodeParents.ContainsKey(node)) continue; queue.Enqueue(node); nodeParents.Add(node, current); // Found a path to the destination if (comparer.Equals(node, destination)) return GetPath(destination, nodeParents).Reverse(); } } return null; } /// /// Walks through a map of nodes from the starting point. /// /// /// /// /// private static IEnumerable GetPath(T start, IDictionary nodeParents) { IcdHashSet visited = new IcdHashSet(); while (true) { yield return start; visited.Add(start); T next; if (!nodeParents.TryGetValue(start, out next)) break; if (visited.Contains(next)) break; start = next; } } } }