反应深度优先搜索
本文关键字:深度优先搜索 | 更新日期: 2023-09-27 18:18:36
我正在尝试开发一个响应式DFS实现,它可以产生不同的节点,而不需要遍历不必要的路径。树可能包含重复/相等的节点。如果一个节点已经被遍历,那么它的所有子节点也都被遍历了,因此如果我们再次遇到一个节点,我们可以停止扩展遍历。
class Traversal {
//exposed method with understandable signature
public IObservable<Node> DepthFirst(Node root) {
return DepthFirstRecursive(root, new List<Node>(), new SomeStatefulHelper());
}
//hidden method with accumulator for cutting
private IObservable<Node> DepthFirstRecursive(Node root, List<Node> accuFilter, SomeHelper helper) {
return root.GetChildren().AsObservable()
//here we cut traversal:
.Where(n => !accuFilter.Contains(n))
//cannot do SelectMany, because we want sequential processing:
.Select(n => DepthFirstRecursive(n, accuFilter, helper)).Concat()
.Concat(Observable.Return(root))
//adding results to accumulator
.Do(accuFilter.Add)
}
}
class Node : IEquatable<Node> {
public IEnumerable<Node> GetChildren(SomeHelper helper) {
//produce child nodes
}
...
}
累加器不美观。它基本上是通过优雅的响应式实现编织的副作用。我如何使用Rx摆脱它? Observable.Distinct()
不会切割它(双关语),因为它只能在链的最后(.Do
所在的地方)操作。
此外,有了帮助器,我在.Where
中的=>
上得到了一个"隐式捕获的闭包:助手"警告。我看不出这个警告有什么意义。是吗?我怎样才能摆脱它?
我认为RX不适合这里的工作。
另外,您应该使用HashSet来跟踪访问的节点。
List.Contains()遍历所有元素,因此您将引入O(n^2)行为。
最简单的方法是使用Enumerable.SelectMany()
不需要依赖额外的类来实现GetChildren(),您可以简单地使用委托Func<T, IEnumerable<T>> getChildren
来指定如何获得所讨论的节点类型的子节点。
我是这样做的:
public static IEnumerable<T> DepthFirst<T>(T node,
Func<T, IEnumerable<T>> getChildren,
HashSet<T> visitedNodes)
{
return getChildren(node)
.Where(visitedNodes.Add)
.SelectMany(child => DepthFirst(child, getChildren, visitedNodes))
.Concat(new[] { node });
}
public static IEnumerable<T> DepthFirst<T>(T root,
Func<T, IEnumerable<T>> getChildren)
{
return DepthFirst(root, getChildren, new HashSet<T> {root});
}