在这种情况下,循环引用检查的优秀算法是什么?

时间:2009-01-09 14:38:24

标签: algorithm

鉴于你有以下课程(糟糕的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。但我想知道是否有更好的,经证实有效的方法来做到这一点,例如添加一个方法来折叠thistarget的整个Children参考树,然后检查结果(更少)堆栈上的压力?),或者可能完全通过使用除List&lt; T&gt;之外的不同(可能是自检!)集合来避免检查?

你会怎么做?

编辑:正如stefan指出的那样,只需要确定目标是否可以达到

2 个答案:

答案 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到达的完整元素集。