我在C#对象中有一个递归数据结构。
对象具有“部件”的集合。每个零件还有一个零件集合。等等。这个结构理论上可以永远嵌套。
object
--> Part
--> Part
--> Part
--> Part
--> Part
--> Part
--> Part
我想得到这个结构中所有部件的数量。所以,所有的分支和叶子。 (在上面的例子中,共有7个部分。)
有没有办法在不初始化计数器并在树中递归的情况下执行此操作?当然,我可以做到这一点,但它看起来很慢而且有点过分。有更好/更简单的方法吗?
答案 0 :(得分:5)
处理递归数据结构的一种自然方式是计数或任何其他类型的聚合,它采用递归函数:
class Part {
IEnumerable<Part> SubParts {get;set;}
public int TotalParts {
get {
return 1 + SubParts.Sum(p => p.TotalParts);
// ^ ^
// | |
// Add one for this part |
// |
// Use LINQ to aggregate counts recursively
}
}
}
该函数假设较低级别的部分不在较高级别的部分之间共享,即部分图形是树。提高这类函数性能的一种方法是将结果缓存在Part
以下的级别,以确保递归计算只进行一次。
您可以通过实现队列或堆栈来避免递归,但实现不会那么简单。
答案 1 :(得分:2)
您可以覆盖每个元素上的Add方法,并在添加元素时在每个Part上保留count属性,类似于List.Count
否则,没有比迭代更准确的方法了。对于大小的估计,您可以采用代表性样本并从中推断出粗略的大小。
答案 2 :(得分:2)
这是一个非递归解决方案,而不是迭代解决方案,它也能够遍历树并计算节点。
public class Part
{
public IEnumerable<Part> Children { get; set; }
public int Count()
{
var stack = new Stack<Part>();
stack.Push(this);
int total = 0;
while (stack.Any())
{
total++;
foreach (var child in stack.Pop().Children)
stack.Push(child);
}
return total;
}
}
答案 3 :(得分:0)
我只是使用递归,但是如果你真的想要避免它,那么你可以覆盖集合的Add / Remove方法,并使用委托给包含对象的方法,该对象更新{{1的单个实例变量(每个包含对象,而不是每个集合)。每次在树中的任何集合中添加或删除部件时,都会通过委托修改totalCount
变量。
每个集合节点都不知道它的父/子节点的数量,并且集合本身隐藏了总计数如何维护的实现。