检测循环引用

发布于 2024-12-09 14:13:39 字数 1417 浏览 0 评论 0原文

所以在一次采访中,我实际上被问到一个简单的问题,如下所示,假设我有一个嵌套的 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 不正确,打印也是如此,因此也在寻找解决该问题的建议..我只是对这个问题感到好奇。

So in an interview, I was actually asked a simple question that goes like this, say that I have a nested JSON response, [a, b, c ,d [a, [b, [d, e], g], h]. I am asked to implement a class that basically can handle to store this data and a print method to do so, so here's what I have:

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);
  }

}

Now he said that there will circular references issues when I print, in other words I have list A = [a, b, c, D] and D = [q, t, y, A]. So he said I'd have to prevent from adding D by using the checkCircular above. I made an attempt. Also just a node I know my recurseJSONode isn't correct and so does the print, so looking for a suggestion to fix that as well.. I am just curious to this problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一个人的旅程 2024-12-16 14:13:39

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

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

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 的优化查找将变得有益。

The reason your circular check isn't right is that you only look for an existing duplicate of JSONode under the one node you're trying to add it to. But A might be under B and B is under A, so each is unique within its parent's list.

Re: using a stack for tracking activity in a recursive function:

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

And then inside 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);
}

The advantage of HashSet is that add and remove typically run in constant time - the size of the tree is irrelevant.

For comparison:

Markus Lausberg's answer suggests doing a complete recursive search of the whole tree, which would be O(N) where N is the number of nodes in the tree, and as you are doing that check for every node it ends up being O(N^2). A tree of 10 nodes will do 100 node comparisons; a tree of 1000 nodes will do 1000,0000 node comparisons.

In kan's answer the check involves searching the parent chain, which will depend on the depth of the tree. For a perfectly lopsided tree (worst case) the depth will be the same as the number of nodes, giving O(N^2) again. For a balanced tree the depth will be ~log N, not much better (remember, the check has to be done for every node).

The effect of these differences depends on the comparison operation used to determine if two nodes are the same. If it is just a pointer comparison (i.e. you only care if they're the same object) and the tree is never very large, the overhead of HashSet may have a negative impact. Whereas if you need to compare two nodes in a more complex way, so each comparison is expensive, and the tree is large, then the optimised lookup of HashSet will become beneficial.

没有心的人 2024-12-16 14:13:39

首先,它应该像

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;
  }

First of all it should be like

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

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

You have to check recursivly, if the given node is part of the parent node and the parent of the parent and so on...

So more like

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;
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文