鉴于你有以下课程(糟糕的C#,但你得到漂移):
public abstract class AmICircular
{
// assume Children is never null
private List<AmICircular> Children {get;set;}
// assume target is never null
public void Add(AmICircular target)
{
target.PerformCircularReferenceCheck(this);
Children.Add(target);
}
// throws when a circular reference is detected
protected abstract void PerformCircularReferenceCheck(AmICircular target);
}
您将如何实施PerformCircularReferenceCheck?而且,不,这不是家庭作业。
天真的实现imo将对this
和所有孩子进行参考检查,然后在target
上调用PerformCircularReferenceCheck,并传递this
。但我想知道是否有更好的,经证实有效的方法来做到这一点,例如添加一个方法来折叠this
和target
的整个Children参考树,然后检查结果(更少)堆栈上的压力?),或者可能完全通过使用除List&lt; T&gt;之外的不同(可能是自检!)集合来避免检查?
你会怎么做?
编辑:正如stefan指出的那样,只需要确定目标是否可以达到
答案 0 :(得分:13)
如果您从未添加自引用(稍后定义)对象,则您的数据结构将描述有向非循环图(http://en.wikipedia.org/wiki/Directed_acyclic_graph),其中IAmCircular类的每个实例都描述一个节点一组直接后继节点=儿童。
假设到目前为止没有创建循环的前提条件,你想要的函数PerformCircularReferenceCheck只需要检查“this”是否可以从“target”到达。如果是,则应返回异常。
复杂性理论明智,这个问题是ST连接(http://en.wikipedia.org/wiki/St-connectivity)并且对于NL类(http://en.wikipedia.org/wiki/NL_(complexity))是完整的,即使你将输入限制为非循环图(这是你的情况)
特别是,Savitch的定理(http://en.wikipedia.org/wiki/Savitch%27s_theorem)提供了一种建设性的方法来为它创建一个O(log ^ 2 n)空间算法(在时间O(n ^ 2)运行),其中n是数字节点。
此外,由于NL-complete,不太可能存在在空间O(log n)中运行的算法(即仅使用指向节点的恒定数量的指针),因为这意味着不可能的NL = L.编辑:特别是,有人建议的野兔和海龟算法的小变化是行不通的(因为他们会占用太少的空间)。
我建议实现平凡的O(n)时间,O(n)空间算法,该算法为“目标”生成其后继集合(递归)并验证此集合中是否出现“this”。
小心,集合的明确构造很重要。否则,如果您只是递归验证“目标”的任何后继者是否可以访问“此”,则可能会以指数时间运行。
我推荐了O(n)时间/ O(n)空间算法,因为它是渐进式的,你可以在时间上做到最好,并且你已经在为数据结构使用O(n)空间了。
答案 1 :(得分:8)
迭代解决方案是定义集合R(可达)和CR(可达的子集)。
您从R = {this}
和CR = {this.children}
开始。
在每个步骤中,检查CR是否包含this
(或target
,具体取决于您的确切目标)。如果没有,则将CR添加到R并将CR设置为CR的子项,并从CR中删除R的元素。
如果CR变空,则R是可从this
到达的完整元素集。