检测循环参考

时间:2011-10-12 13:55:56

标签: java recursion

所以在接受采访时,我实际上被问到一个类似这样的简单问题,说我有一个嵌套的JSON响应,[a,b,c,d [a,[b,[d,e],g ], H]。我被要求实现一个基本上可以处理来存储这些数据的类和一个打印方法,所以这就是我所拥有的:

public class JSONode
{
   private String str;
   private JSONode nodes;

   public JSONode (String a, ArrayList<String> n)
   {
        str = a;
        nodes = n;
   }
}

public class JSONResp
{
  private ArrayList<JSONode> arr;

  public JSONResp ()
  {
    arr = new ArrayList<JSONode>();
  }

  public boolean checkCircular(JSONode temp)
  {
    for (int i = 0; i < arr.size(); i++)
    {
        if (arr.get(i).nodes == temp)
            return true;
    }
    return false;
  }

  public void add (JSONode nd)
  {
    if (!checkCircular(nd))
    arr.add(nd);
  }

  public void recurseJSONode(JSONode)
  {
      if (!JSONode.node)
           System.out.print(JSONode.str);
      else {
          System.out.print(JSONode.str);
          recurseJSONode(JSONode.node);
      }
  }

  public void print()
  {
    for (int i = 0; i < arr.size(); i++)
       recurseJSONode(arr.get(i));
  }

  public static void main (String[] args) {
      JSONResp x = new JSONResp();
      x.add(new JSONode("a", null);
      x.add(new JSONode("b", null);
  }

}

现在他说我打印时会出现循环引用问题,换句话说我有列表A = [a,b,c,D]和D = [q,t,y,A]。所以他说我必须通过使用上面的checkCircular来阻止添加D.我做了一个尝试。也只是一个节点,我知道我的recurseJSONode是不正确的,打印也是如此,所以寻找一个建议来解决它...我只是好奇这个问题。

2 个答案:

答案 0 :(得分:3)

您的循环检查不正确的原因是您只在您尝试将其添加到的一个节点下查找现有的JSONode副本。但A可能在B之下而B在A之下,所以每个在其父级列表中都是唯一的。

Re:使用堆栈跟踪递归函数中的活动:

Set<SomeNodeType> stack = new HashSet<SomeNodeType>();
someRecursiveThing(rootNode, stack);

然后在someRecursiveThing里面:

void someRecursiveThing(SomeNodeType under, Set<SomeNodeType> stack) {

    if (!stack.add(under)) {
        return;

    // continue happily, e.g. call self with child node,
    // passing down the stack

    SomeNodeType child = ...
    someRecursiveThing(child, stack)

    // finish by removing 'under' from the stack:
    stack.remove(under);
}

HashSet的优点是addremove通常在恒定时间内运行 - 树的大小无关紧要。

进行比较:

Markus Lausberg的回答建议对整个树进行完整的递归搜索,这将是O(N),其中N是树中节点的数量,并且当您检查每个节点时,它最终为O. (N ^ 2)。 10个节点的树将进行100个节点比较;一个1000个节点的树将进行1000,0000个节点比较。

在kan的回答中,检查涉及搜索父链,这将取决于树的深度。对于完全不对称的树(最坏情况),深度将与节点数相同,再次给出O(N ^ 2)。对于平衡树,深度将为~log N,并不是更好(请记住,必须为每个节点进行检查)。

这些差异的影响取决于用于确定两个节点是否相同的比较操作。如果它只是一个指针比较(即你只关心它们是否是同一个对象)并且树永远不会很大,HashSet的开销可能会产生负面影响。然而,如果您需要以更复杂的方式比较两个节点,因此每次比较都很昂贵,并且树很大,那么HashSet的优化查找将会变得有益。

答案 1 :(得分:3)

首先它应该像

public class JSONode
{
   private String str;
   private ArrayList<JSONode> nodes;

   public JSONode (String a, ArrayList<JSONode> n)
   {
        str = a;
        nodes = n;
   }
}

如果给定节点是父节点的一部分和父节点的父节点,则必须以递归方式检查,等等......

更像是

public static boolean checkCircular(JSONode temp)
  {
    if(temp == null){
       return false;
    }

    ArrayList<JSONode> pNodes = temp.getChildrens();

    for (int i = 0; i < nodes.size(); i++)
    {
        if (pNodes.get(i).getString().equals(temp.getString()))
            return true;
        if(checkCircular(temp))
            return true;
    }
    return false;
  }