我有以下(其混凝土):
public interface IDirectory
{
IEnumerable<IDirectory> Directories{get;}
}
我深入构建了一个图形 n 目录。现在我想枚举从根开始并递归到终端/最深目录的目录,我必须按顺序进行,I.E。在返回目录之前永远不会返回目录。我确信有一个优雅的解决方案,但它在我的时区已经很晚了,我的灰质已经累了,所以任何指针都非常赞赏。
答案 0 :(得分:9)
另外两个解决方案是这个问题的典型递归解决方案,适用于没有周期的浅图。如果你有一个带有周期的深图,那么它们就不会有效。你最好用这样的东西:
public static IEnumerable<T> DepthFirstTraversal<T>(
T root,
Func<T, IEnumerable<T>> children)
{
var set = new HashSet<T>();
var stack = new Stack<T>();
stack.Push(root);
while(stack.Count != 0)
{
T current = stack.Pop();
if (set.Contains(current)) continue;
yield return current;
set.Add(current);
foreach(var child in children(current))
stack.Push(child);
}
}
然后用
调用它foreach(var d in DepthFirstTraversal(root, x=>x.Directories))
...
该集合跟踪已经访问过的节点,因此占用了O(n)空间。没有递归,所以它在堆栈空间中是O(1)。
如果您知道没有周期,则可以消除该设置并节省该费用。
答案 1 :(得分:4)
IEnumerable<IDirectory> Enumerate(IDirectory parent)
{
yield return parent;
foreach (var child in parent.Directories)
foreach (var directory in Enumerate(child))
yield return directory;
}
不是最有效的,但它有效。 Eric Lippert撰写了一些关于如何使用Stack<IDirectory>
而不是递归方法调用来更有效地(但不那么简洁)地执行此操作的项目。
答案 2 :(得分:4)
更短的版本:
IEnumerable<IDirectory> Enumerate(IDirectory parent)
{
return new[] { parent }.Concat(parent.Directories.SelectMany(Enumerate));
}
警告:此解决方案(以及发布的另一个解决方案)不防范无限循环。理论上可以在对象结构中使用循环引用,在这种情况下,这将永远继续下去并永不停止。