分配树节点时出现溢出问题

时间:2016-04-20 01:50:37

标签: java recursion tree

我目前正在处理一个问题,需要我根据给定的顶点对生成一个n-ary树,这些顶点对是从输入文件读入的。这是我当前的Node类:

public class Node<T> {
    private int idx;
    private int data;
    private Node<T> parent;
    private List<Node<T>> children;

    // Template for new nodes
    public Node() {
        this.idx = 1;
        this.data = 0;
        this.parent = null;
        this.children = new ArrayList<Node<T>>();
    }

    public void insertNode(Node<T> currNode, int leftIdx, int rightIdx) {
        if (currNode.idx == leftIdx) {
            Node<T> childNode = new Node<T>();
            childNode.idx = rightIdx;
            childNode.parent = currNode;
            currNode.children.add(childNode);            
            return;
        }

        for (int i = 0; i < currNode.children.size(); i++) {
            Node<T> tempNode = currNode.children.get(i);
            if (tempNode.idx == leftIdx ) {
                Node<T> childNode = new Node<T>();
                childNode.idx = rightIdx;
                childNode.parent = tempNode;
                tempNode.children.add(childNode);            
                return;
            }
        }

        Node<T> upCurrNode = currNode.children.get(currNode.children.size() - 1);
        upCurrNode.insertNode(upCurrNode, leftIdx, rightIdx);
        return;
    }          
} 

正如您所看到的,我的insertNode方法是递归调用的,以便用所有节点填充整个树,没有数据。还有其他方法可以为这些节点添加值,但输入文件分为两部分 - 首先创建节点,然后执行查询,以便稍后更新每个节点内的数据(我无法控制)。

我用两个输入文件运行它,一个在树中有大约一百个节点,另一个大两个数量级。较小的树工作正常,但是在使用较大的树进行测试时出现以下错误,这在第2050次递归调用之后发生:

Node: Exception in thread "main" java.lang.StackOverflowError
        at sun.nio.cs.SingleByte.withResult(Unknown Source)
        at sun.nio.cs.SingleByte.access$000(Unknown Source)
        at sun.nio.cs.SingleByte$Encoder.encodeArrayLoop(Unknown Source)
        at sun.nio.cs.SingleByte$Encoder.encodeLoop(Unknown Source)
        at java.nio.charset.CharsetEncoder.encode(Unknown Source)
        at sun.nio.cs.StreamEncoder.implWrite(Unknown Source)
        at sun.nio.cs.StreamEncoder.write(Unknown Source)
        at java.io.OutputStreamWriter.write(Unknown Source)
        at java.io.BufferedWriter.flushBuffer(Unknown Source)
        at java.io.PrintStream.write(Unknown Source)
        at java.io.PrintStream.print(Unknown Source)
        at java.io.PrintStream.append(Unknown Source)
        at java.io.PrintStream.append(Unknown Source)
        at java.util.Formatter$FormatSpecifier.print(Unknown Source)
        at java.util.Formatter$FormatSpecifier.print(Unknown Source)
        at java.util.Formatter$FormatSpecifier.printInteger(Unknown Source)
        at java.util.Formatter$FormatSpecifier.print(Unknown Source)
        at java.util.Formatter.format(Unknown Source)
        at java.io.PrintStream.format(Unknown Source)
        at node.Node.insertNode(Node.java:38)
        at node.Node.insertNode(Node.java:44)
        at node.Node.insertNode(Node.java:44)
        at node.Node.insertNode(Node.java:44)
        at node.Node.insertNode(Node.java:44)
        at node.Node.insertNode(Node.java:44)...
        (repeats for a couple thousand lines)

第38行是System.out.format("Node: %d Parent: %d\n", childNode.idx, childNode.parent.idx);,而第34行是递归insertNode来电:upCurrNode.insertNode(upCurrNode, leftIdx, rightIdx);

我对此问题的最佳猜测是,程序根据抛出的StackOverflowError异常溢出了JVM的默认内存分配。可以理解的是,我还可以看到每个节点的内存开销非常高:idx字段为4个字节,data为4个字节,另外4个为parent节点的引用,以及40字节+ ArrayList本身为children创建默认值所需的开销所需的任何数量。假设每个节点所需的总内存大约为70KB,那么树的总大小将大约为700MB,这对于这样一个简单的程序来说是荒谬的(顺便说一下,我错了;请参阅下面的评论)

所以,我现在的问题是:我的insertNode方法是否效率低下?有没有办法优化它,以便它可以处理大量数据,而不需要运行程序的人传递一个允许增加内存使用的特殊标志?谢谢

编辑:这是关于如何首次生成树的代码段:

Node<Integer> tree = new Node<Integer>();  
String line = null; // line reader
// temp variables for processing nodes during tree generation
int leftNodeIdx, rightNodeIdx; 
// String array for processing node indexes
String[] twoNodesIdx;
// first line is the number of nodes    
int numN = Integer.parseInt( bufferedReader.readLine() ); 
// Populate the tree with child nodes as specified in the input_file 
for (int i = 0; i < (numN - 1); i++) {
    line = bufferedReader.readLine();
    twoNodesIdx = line.split("\\s");
    leftNodeIdx = Integer.parseInt(twoNodesIdx[0]);
    rightNodeIdx = Integer.parseInt(twoNodesIdx[1]);
    tree.insertNode(tree, leftNodeIdx, rightNodeIdx);
}

0 个答案:

没有答案