【问题标题】:How to stop tree search after child was found找到孩子后如何停止树搜索
【发布时间】:2013-12-16 21:18:15
【问题描述】:

以下内容未能返回正确的子节点,即使它实际上是在树的更远位置找到子节点。它似乎在找到孩子后放弃了它,继续搜索树的其余部分。

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 的孩子,即使我在树中添加了一个。

【问题讨论】:

  • if (root.getKey().equals(key)) { return root; } 应该在 if (children != null) 块之外

标签: java tree tree-search


【解决方案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)在没有子节点的节点上完成的。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 如果getNode 返回null,这将不起作用。例如,如果一个节点有两个子节点,并且所需的项目在第二个子节点中,则此修改将返回null,因为getNode 将为第一个子节点返回null
    【解决方案3】:

    经过重大的重新格式化工作,我已将您的代码重构为更清晰的形式。虽然大不相同,但以下内容在逻辑上与您的代码相同:

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

    现在我们已经摆脱了这么多代码,问题实际上是相当明显的:upper 方法实际上从未对通过搜索子节点出现的节点做任何事情。不过,一个简单的更改就足以解决此问题:

    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!你不应该有一个,它的所有功能都应该完全存在于节点类中。理想情况下,根节点树。

    【讨论】:

      猜你喜欢
      • 2022-01-01
      • 2021-06-29
      • 1970-01-01
      • 1970-01-01
      • 2020-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-21
      相关资源
      最近更新 更多