简体   繁体   English

尝试DFS此图时为什么会出现StackOverFlowError?

[英]Why do I get StackOverFlowError when trying to DFS this graph?

I am trying to write an algorithm that determines whether a graph is connected or not. 我正在尝试编写一种算法来确定是否连接了图形。 I think my code is almost correct, although I keep getting StackOverFlowError. 我认为我的代码几乎是正确的,尽管我不断收到StackOverFlowError。 I personally think because there's a cycle in the graph I'm testing my algorithm with, my code doesn't understand that and comes in a loop. 我个人认为,因为在图中测试我的算法的过程中存在一个循环,所以我的代码不了解这一点,因此会陷入循环。 But I'm using an array to see if a node was already visited! 但是我正在使用数组来查看是否已访问节点! So that should not happen! 所以那不应该发生! Anyways this is my code: 无论如何,这是我的代码:

public int isConnected(String s) 
    {

        int in = nodes.indexOf(s);


        visited[in] = true;
        counter++;
        for(int i = 0; i < listOfChildren(s).size(); i++)
        {
            int ind = nodes.indexOf(listOfChildren(s).get(i));
            if(visited[ind] == false)
            {
                isConnected(listOfChildren(s).get(i));
            }

        }
        System.out.println(counter);
        if(counter == nodes.size())
            return 1;
        return 0;

    }

s is some node I begin with, nodes is an ArrayList of nodes and has the same size(5 in this case) as the array visited. s是我开始的某个节点,nodes是节点的ArrayList,并且具有与访问的数组相同的大小(在本例中为5)。 In the beginning visited looks like this: [false false false false false], so none of the nodes was visited. 在开始访问时,看起来像这样:[false false false false false],因此没有节点被访问过。 listOfChildren() return an ArrayList of the children(not all of them, just the ones adjacent to the node) of a particular node. listOfChildren()返回特定节点的子节点(不是所有子节点,而是所有与节点相邻的子节点)的ArrayList。 I am sure that listOfChildren() works, since I tested it 43545454 times. 我确定listOfChildren()可以正常工作,因为我对其进行了43545454次测试。

Any help is appreciated(with minimum change of the code, if possible). 感谢您的任何帮助(如果可能,请尽量减少代码更改)。 Thanks. 谢谢。

UPDATE: 更新:

My graph is directed.. 我的图是有向的。

I define visited like this: 我定义这样访问:

private boolean[] visited;

and I set everything in it to false in my constructor this code: 然后在我的构造函数中将此代码中的所有内容设置为false:

public void setUnvisited()
    {
        visited =  new boolean[nodes.size()];

        for(int i = 0; i < nodes.size(); i++)
        {
            visited[i] = false;
        }
    }

The nodes are strings! 节点是字符串! visited and nodes have the same length. 访问的节点和节点的长度相同。 That's why I can use nodes.indexOf(blabla) for the array visited. 这就是为什么我可以将nodes.indexOf(blabla)用于访问的数组。

UPDATE2: UPDATE2:

This is how the graph looks like: 图是这样的: 在此处输入图片说明

I'm sure the problem is after N3, the algorithm goes in a loop after N3, because it doesn't understand that N1 was already visited. 我确定问题出在N3之后,该算法在N3之后进入循环,因为它不了解N1已经被访问过。 I really don't understand why this happens! 我真的不明白为什么会这样!

UPDATE3 UPDATE3

String have different names and there are no duplicates.. so for example indexOf(nodes.get(2)) gives 2 and not 0 or anything else.. 字符串具有不同的名称,并且没有重复..因此,例如indexOf(nodes.get(2))给出2而不是0或其他。

The problem is that after visiting N3 the algorithm should stop and return 0 or 1, but I don't know how to do that :) 问题是在访问N3之后,算法应停止并返回0或1,但我不知道该怎么做:)

The reason you found this so difficult to track down is all the mutable state in your graph class. 您发现很难追踪的原因是图形类中的所有可变状态。 In particular you have count and visited , the latter being modified by both your methods isConnected and setUnvisited . 特别是您已经count并且visited ,后者由isConnectedsetUnvisited方法修改。

You depend on your array visited to tell you when to stop recursing, but accidentally reset the array on every recursive call through your call to listOfChildren . 您依赖于visited的数组来告诉您何时停止递归,但是通过对listOfChildren的调用,在每次递归调用上意外重置了数组。

One way around this to make it so that visited is not a class member. 解决这个问题的一种方法是使visited不是班级成员。 Here's a solution which modifies your code very little: 这是一个几乎不修改代码的解决方案:

public boolean isConnected(String s) {
    int nVisited = isConnected(s, new boolean[nodes.size()], 0);
    return nVisited == nodes.size();
}

private int isConnected(String s, boolean[] visited, int counter) 
{

    int in = nodes.indexOf(s);


    visited[in] = true;
    counter++;
    for(int i = 0; i < listOfChildren(s).size(); i++)
    {
        int ind = nodes.indexOf(listOfChildren(s).get(i));
        if(visited[ind] == false)
        {
            counter = isConnected(listOfChildren(s).get(i), visited, counter);
        }

    }
    System.out.println(counter);
    return counter;
}

Since visited and counter are no longer shared, the bug you had is gone. 由于visitedcounter不再共享,因此您所遇到的错误已消失。 This also solves another bug you had (but didn't notice yet) where only the first isConnected() call works--because in that case you weren't resetting visited or counter appropriately. 这也解决了另一个错误(但尚未引起注意),在该错误中只有第一个isConnected()调用有效-因为在这种情况下,您没有适当地重置visitedcounter

A cleaner implementation of the same idea as above: 与上述相同的想法的更干净的实现:

public boolean isConnected(String s) {
    Set<String> visited = new HashSet<String>();
    isConnected(s, visited);
    return visited.size() == nodes.size();
}

private void isConnected(String s, Set<String> visited) 
{
    visited.add(s);
    for (String child : listOfChildren(s)) {
        if (!visited.contains(s)) {
            isConnected(child, visited);
        }
    }
}

I haven't actually tried to compile or run that, so there may be bugs, but you get the idea, I hope. 我实际上并未尝试编译或运行该程序,因此可能存在错误,但是我希望您能理解。

I made a small test program based on your updates, and it seems to work like a charm: 我根据您的更新制作了一个小型测试程序,它看起来像一个魅力:

public class NodeTest
{
    static ArrayList<String> nodes = new ArrayList<String>();
    boolean visited[] = {false, false, false, false, false};

    int counter = 0;

    static HashMap<String, ArrayList<String>> childMap = new HashMap<String, ArrayList<String>>();

    static
    {
        nodes.add("N0");
        nodes.add("N1");
        nodes.add("N2");
        nodes.add("N3");
        nodes.add("N4");

        //N4 --> N2 --> N3 --> N1 <-- N0
        //       ^-------------+
        ArrayList<String> list = new ArrayList<String>();
        list.add("N2");
        childMap.put("N4", list); //N4 to N2

        list = new ArrayList<String>();
        list.add("N3"); 
        childMap.put("N2", list); //N2 to N3

        list = new ArrayList<String>();
        list.add("N1");
        childMap.put("N3", list); //N3 to N1

        list = new ArrayList<String>();
        list.add("N2");
        childMap.put("N1", list); //N1 to N2

        list = new ArrayList<String>();
        list.add("N1");
        childMap.put("N0", list); //N0 to N1
    }

    @Test
    public void test()
    {
        System.out.println("Is connected = " + isConnected("N0"));
    }

    public int isConnected(String s) 
    {
        System.out.println("Handling node " + s);

        int in = nodes.indexOf(s);


        visited[in] = true;
        counter++;
        for(int i = 0; i < listOfChildren(s).size(); i++)
        {
            int ind = nodes.indexOf(listOfChildren(s).get(i));
            if(visited[ind] == false)
            {
                System.out.println("Recursing into child " + listOfChildren(s).get(i));
                isConnected(listOfChildren(s).get(i));
            }
            else
            {
                System.out.println("Node " + listOfChildren(s).get(i) + " has already been visited");
            }

        }
        //System.out.println(counter);
        if(counter == nodes.size())
            return 1;
        return 0;

    }

    public ArrayList<String> listOfChildren(String s)
    {
        return childMap.get(s);
    }

}

The isConnected-method is same as yours, I just added some messages for logging. isConnected方法与您的方法相同,我只是添加了一些用于记录的消息。 Output: 输出:

Handling node N0
Recursing into child N1
Handling node N1
Recursing into child N2
Handling node N2
Recursing into child N3
Handling node N3
Node N1 has already been visited
Is connected = 0

As expected, the graph is not connected (it's the same graph you drew on your question). 不出所料,该图未连接(与您在问题上绘制的图相同)。 If I change the starting node to N4 and change N1's only child to N0, the algorithm correctly recognizes the graph as connected. 如果我将起始节点更改为N4并将N1的唯一子节点更改为N0,则该算法会正确地将图识别为已连接。 Based on this, I'd say the problem is either the listOfChildren returning something wacky or the indices used with visited (at some point, visited[in] = true marks some other index as visited than if(visited[ind] == false) is checking against when they should be the same?). 基于此,我想说的问题是,要么访问使用返回的东西古怪的listOfChildren或指数(在某些时候,如走访了如果 参观[中] =真正的标记其他一些指数(参观[IND] ==假)正在检查它们何时应该相同?)。

The real problem is, you are trying to represent a Node by a String. 真正的问题是,您试图用字符串表示Node。 By doing that, you have to store the children of a Node somewhere else, listOfChildren . 这样,您必须将Node的孩子存储在其他位置listOfChildren You also need to keep track of who you visited, boolean[] visited . 您还需要跟踪访问过的人boolean[] visited

A node is now identified in two ways 现在可以通过两种方式识别节点

  1. listOfChildren uses the String representation "node1","node2",... listOfChildren使用字符串表示形式"node1","node2",...
  2. visited[] uses an index, the index in nodes . visit []使用索引, nodes的索引。

Oho. 大穗。 Now, you must be sure, that every String representation has exactly one index in the nodes array. 现在,您必须确保,每个String表示形式在nodes数组中都只有一个索引。 (There must be a one-to-one mapping of the two identifications.) (两个标识必须一一对应。)

nodes.add("node");
nodes.add("node");
nodes.add("node");

return nodes.indexOf( nodes.get(2) );//what does this return? 0!

See the problem? 看到问题了吗? There is one 'node' with three indices, or there are three nodes with the same name! 有一个带有三个索引的'node' ,或者有三个同名节点!

But I'm using an array to see if a node was already visited! 但是我正在使用数组来查看是否已访问节点!

Maybe that is not the same 'node', do you mean the String 'node', or the index 'node'? 也许这不是相同的“节点”,您是指字符串“节点”还是索引“节点”?

To fix this make one identification, one representation, for a node: 要解决此问题,请为节点做一个标识( 一种表示):

public class Node
{
  List<Node> children;
}

No String, or index needed! 无需字符串或索引! Just let node be a Node. 只要让节点成为一个节点即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 尝试DFS此图以查找强连接的组件时,为什么会出现StackOverFlowError? - Why do I get StackOverFlowError when trying to DFS this graph for finding strongly connected component? 为什么会出现StackOverflowError - Why do i get a StackOverflowError 为什么我在这里得到StackOverflowError? - Why do I get StackOverflowError here? 为什么在使用Flood Fill算法时出现java.lang.StackOverflowError? - Why do I get java.lang.StackOverflowError when using Flood Fill algorithm? 为什么我在这里得到java.lang.StackOverflowError? - why do i get java.lang.StackOverflowError here? 使用DFS获取图形的清晰度 - Get articulations of a graph with DFS 为什么当我尝试在它自己的 class 中创建 object 时显示 stackoverflowerror? - Why is it showing stackoverflowerror when i am trying to create an object in it's own class? 尝试在 Java 中生成非常大的 PDF 文件时出现 stackOverflowError - I get a stackOverflowError when trying to generate a very large PDF file in Java 尝试创建新控制台时为什么会出现异常? - Why do I get an exception when trying to create a new Console? 尝试加密字符串时为什么会出现BadPaddingException? - Why do I get BadPaddingException when trying to encrypt a string?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM