【问题标题】:Stackoverflow error when recursively searching in a tree在树中递归搜索时出现 Stackoverflow 错误
【发布时间】:2014-03-17 13:00:25
【问题描述】:

我正在使用 Java 实现 Shannon/Fano 算法,我通过计算文本文件中符号的频率来做到这一点,然后我将所有这些值放在树中。问题是,当我在树中搜索某个符号时,我还必须更新相应符号的代码(例如,如果我向左追加 0,否则为 1)并递归地执行此操作,我得到一个 stackoverflow 错误。以下是我的代码:

private String getNodeValue(Node node, String symbol) {
    if (node.getLeftChild() != null) {
        if (node.getLeftChild().getData().equalsIgnoreCase(symbol)) {
            node.updateCode(0);
            return node.getData() + "";
        }
    } else if (node.getRightChild() != null) {
        if (node.getRightChild().getData().equalsIgnoreCase(symbol)) {
            node.updateCode(1);
            return node.getData() + "";
        }
    }


    Node nextLeftNode = node.getLeftChild().getLeftChild();
    if (nextLeftNode != null) {
        getNodeValue(nextLeftNode, symbol);
    }

    Node nextRightNode = node.getRightChild().getRightChild();
    if (nextRightNode != null) {
        getNodeValue(nextRightNode, symbol);
    }

    // if symbol is not found return null
    return null;
}

当调用 node.getData() 时,stackoverflow 错误会在方法的第一行触发。这是我的堆栈跟踪:

Exception in thread "main" java.lang.StackOverflowError
at ro.uvt.it.datastractures.Node.getData(Node.java:47)
at ro.uvt.it.datastractures.Node.getData(Node.java:47)
at ro.uvt.it.datastractures.Node.getData(Node.java:47)

这是我的 getData() 方法:

public String getData() {
    return this.getData();
}

任何帮助或提示将不胜感激, 谢谢。

【问题讨论】:

  • .getData() 是做什么的?您可能在某个地方处于非常深的循环或无限循环中。
  • .getData() 只是一个获取数据属性的 getter 方法,它是一个字符串。我不知道...奇怪的是,如果我使用调试器,异常会在第一行引发,因此不会到达递归调用。
  • 在 .getData() 函数中设置断点会发生什么?
  • 在我看来,您的第二个 if 语句 if (((String) node.getData()).equalsIgnoreCase(symbol)) 与第一个 if (node.getData().equalsIgnoreCase(symbol)) 完全相同。你在这两种情况下都使用node.getData(),因为它已经返回了一个字符串,所以在第二个 if 语句中再次将它转换为一个字符串不会有什么不同。
  • 正如@OMGtechy 所暗示的,递归在getData 方法中,而不是在getNodeValue 方法中,所以你在这里发布了错误的方法。

标签: java tree recursive-datastructures


【解决方案1】:

你的代码有很多错误。

正如您在堆栈跟踪中显示的那样,无限递归在getData 方法中而不是在getNodeValue 方法中,因此您需要发布getData 方法的源代码。

但是getNodeValue 方法也有很多错误。

您的前两个 if 语句具有完全相同的条件:

if (node.getData().equalsIgnoreCase(symbol)) {

else if (((String) node.getData()).equalsIgnoreCase(symbol)) {

这些 if 语句中的 return 将一个空字符串附加到 getData() 的结果中,它已经返回 String。将它们中的每一个替换为:

return node.getData();

只是不同的写法,因为 getData() 已经返回一个字符串,所以再次转换为字符串并没有什么区别。

你的下一个 if 语句在 leftChildrightChild 上递归调用 getNodeValue,但它们从不返回任何东西,所以一旦你通过了前两个相同的 if 语句,你总是最终在代码中返回 null你的方法。

你的代码应该是这样的:

if (node.getLeftChild() != null) {
    String found = getNodeValue(node.getLeftChild(), symbol);
    if (found != null) {
        return found;
    }
}
if (node.getRightChild() != null) {
    String found = getNodeValue(node.getRightChild(), symbol);
    if (found != null) {
        return found;
    }
}

【讨论】:

  • @Costy 堆栈溢出来自 this.getData();在您的 getData 函数中。它基本上是在调用自己而不是返回任何数据。上面的代码只是因为你不再调用 getData 才有效,但是请删除该函数,因为它会在你下次调用它时产生相同的 stackOverflow。
  • @David 我的最后一个代码部分只是替换了第三个和第四个 if 语句,而不是整个方法(在我发布答案后,OP 中的代码已更改,我不是确定为什么它现在调用node.getLeftChild().getLeftChild() 而不仅仅是node.getLeftChild())
  • @ErwinBolwidt 你的建议很好。他还发布了他的 GetData 函数,这是一个问题,因为它所做的只是调用自己。
  • 是的,你们俩都是对的,抱歉这么愚蠢...谢谢大家的回答,感谢你们浪费了我的时间。我真的很感激。
【解决方案2】:

几乎可以肯定是无限递归。我猜我会说这是getLeftChildgetRightChild 的实现中的一个错误。我建议你在调试器中单步调试,我敢打赌你会很快看到它的去向。

但是,如果你有一个非常深的树,你可能会发现堆栈溢出异常是合法的,在这种情况下你需要重新访问算法。而传统的尾递归技术appears to be hard to achieve in Java.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-10
    • 2011-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多