如何在找到孩子后停止树搜索

时间:2013-12-16 21:18:15

标签: java tree tree-search

以下是无法返回正确的子节点,即使它实际上是在树的上方找到了孩子。它似乎在发现后让孩子掉线,广告继续搜索树的其余部分。

private Node<K, V> getNode(K key, ArrayList<Node<K, V>> children){
    if (children == null) return null;

    if (root.getKey().equals(key)) return root;

    for (Node<K, V> child : children) {
        if (child.getKey().equals(key)) return child;
        getNode(key, child.getChildren());
    }

    return null;
}

我用以下代码测试了它:

Tree<Integer, String> tree = new Tree<>(1, "1");

tree.addChild(1, new Node<>(2, "2"));
tree.addChild(1, new Node<>(3, "3"));
tree.addChild(1, new Node<>(4, "4"));
tree.addChild(2, new Node<>(5, "5"));

System.out.println(tree.addChild(5, new Node<>(6, "6")));
System.out.println(tree.addChild(5, new Node<>(7, "7")));

但是,控制台两次输出false,即使它应该是true。无法找到具有5键的子项,即使我在树中添加了一个。

3 个答案:

答案 0 :(得分:1)

问题在于,当您在子树中查找子项时,忽略返回值:

if (child.getKey().equals(key))
{
    // This is fine
    return child;
}
else
{
    // This is bad: you ignore the return value.
    getNode(key, child.getChildren());
}

要修复,请捕获返回值,如果不是null则返回,如下所示:

if (child.getKey().equals(key))
{
    return child;
}
else
{
    Node<K, V> res = getNode(key, child.getChildren());
    if (res != null) {
        return res;
    }
}

此外,当代码存储在根目录中时,您的代码将会错过这种情况,并且根目录中没有子代,因为root.getKey().equals(key) 在没有子代的节点上完成孩子。

答案 1 :(得分:1)

return getNode(key, child.getChildren())语句中写else。它是使用递归的方式。

      ....
      else
      {
       return getNode(key, child.getChildren());
      }

答案 2 :(得分:1)

在重新格式化工作之后,我将您的代码重构为更易读的形式。虽然差别很大,但以下内容在逻辑上与您的代码完全相同:

private Node<K, V> getNode(K key, ArrayList<Node<K, V>> children){
    if (children == null) return null;

    if (root.getKey().equals(key)) return root;

    for (Node<K,V> child : children) {
        if (child.getKey().equals(key)) return child;
        getNode(key, child.getChildren());
    }

    return null;
}

现在,我们可以挑选代码并修复它。

第一个问题是,您在方法前面没有javadoc注释,记录其参数并使用@param@return返回值。这是你需要解决的问题。

其次,此方法应该作为Node类的类方法实现,并且应该是public。那就是:

class Node<K,V> {

// ... whatever else this class has in it ... 

    public K getKey() { /* ... stuff ... */ }

    ArrayList<Node<K,V>> children = new ArrayList<>();

    public Node<K, V> getNode(K key){
        if (children == null) return null;

        if (key.equals(this.getKey())) return this;

        for (Node<K,V> child : children) {
            if (child.getKey().equals(key)) return child;
            child.getNode(key);
        }

        return null;
    }
}

此外,由于我们现在保证children始终被初始化,并且完全在我们的控制之下,我们可以摆脱伪造的null检查。

public Node<K, V> getNode(K key){    
    if (key.equals(this.getKey())) return this;

    for (Node<K,V> child : children) {
        if (child.getKey().equals(key)) return child;
        child.getNode(key);
    }

    return null;
}

现在,你正在冗余地检查孩子们。由于getNode()已经检查this是否是正确的节点,因此没有理由单独检查当前节点的每个子节点:

public Node<K, V> getNode(K key){    
    if (key.equals(this.getKey())) return this;

    for (Node<K,V> child : children)
        child.getNode(key);

    return null;
}

现在我们已经摆脱了这么多代码,问题实际上应该是相当明显的:上层方法实际上从未通过搜索子节点对节点进行任何操作。一个简单的改变足以解决这个问题:

public Node<K, V> getNode(K key){    
    if (key.equals(this.getKey())) return this;

    for (Node<K,V> child : children){
        Node<K,V> result = child.getNode(key);
        if(result != null) return result;
    }

    return null;
}

请注意,我们不应该检查孩子是否null。这应该由我们公开的用于添加新值的方法来处理,我们永远不应该将外部节点添加到树中:

public boolean put(K key, V value){
    children.add(new Node<>(key, value));
}

还有一件事:根本不需要单独的Tree课程 !你不应该有一个,它的所有功能都应该完全存在于节点类中。理想情况下,根节点 树。