在具有许多不同类型节点的树结构中实现搜索功能

时间:2013-06-13 20:11:02

标签: java

我有一个树结构,由几十种类型的节点组成(每种类型的节点都继承自NodeBase类)。

我想在树上执行搜索以返回对特定节点的引用。例如,假设有一些Company树,其中包含Department个节点以及其他类型的节点。 Department个节点由Employee个节点组成。假设员工必须是部门的一部分,并且可以只在一个部门中。

目前,它的设计使每个节点都有一个类型为NodeBase的子节点列表。树可能会变得非常大,有时会有数十万个节点。插入/删除操作很少使用,而搜索操作对于这些大树来说不应该“太长”。

假设我想获得对employee ID字段等于我提供的字符串的员工节点的引用。我不知道该员工在哪个部门,所以我必须搜索所有希望找到匹配的节点。并非所有节点都有employee ID字段;例如,部门没有它们。

鉴于这种树结构设计,我不确定实现搜索功能的最佳方法是什么。

首先可能有更好的方法来设计数据的存储方式(例如:使用数据库?)但是目前我被一棵树困住了。

3 个答案:

答案 0 :(得分:2)

数据结构是您组织数据的方式,您组织数据的方式取决于您实际使用这些信息的方式。

树是回答“获取节点X的所有后代”等问题的正确数据结构,但无法帮助您解决“找到我的对象的问题”属性X设置为Y “(至少不是你的树:你当然可以在内部使用树来保存排序的索引,我稍后会解释)。

所以我认为解决这个问题的最佳方法是使用两个独立的数据结构来组织数据:由NodeBase个对象组成的树,以反映NodeBase's和排序的 index 使搜索具有不错的性能。但是,这会引入同步问题,因为在添加/删除节点时,您必须保持两个数据结构同步。如果它不是经常发生,或者只是搜索性能很关键,那么这可能是正确的方法。

答案 1 :(得分:1)

假设您的树是 DAG (有向非循环树),请使用DFS或BFS。这是一个简单的BFS:

public NodeBase findEmployee (NodeBase root, Integer employeeId) {
    Queue<NodeBase> q= new LinkedList<NodeBase>();
    q.add(root);
    while (!q.isEmpty()) {
        NodeBase node= q.poll();
        if (node instanceof Employee) {
            if (((Employee)node).getId().equals(employeeId))
                return node;
        }
        for (NodeBase child : node.getChildren())
            q.add(child);
        }
    }
}

编辑:访客模式

或者建议Brabster,您可以使用访问者模式。 NodeBase应该实现accept(IVisitor visitor)方法:

public class NodeBase {
    //your code
    public void accept(IVisitor visitor) {
        visitor.visit(this); 
        for (NodeBase node : getChildren()) {
            node.accept(visitor);
        }
    }
}

IVisitor只是一个间隙:

public interface IVisitor {
     public void visit(NodeBase node);
}

你需要一个适当的实现来进行搜索:

public class SearchVisitor implements IVisitor {

     private Integer searchId;

     public SearchVisitor(Integer searchId) {
          this.searchId= searchId;
     }

     @Override
     public void visit(NodeBase node) {
         if (node instanceof Employee) {
             if (((Employee)node).getId().equals(searchId)) {
                  System.out.println("Found the node " + node.toString() + "!");
             }
         }
     }
}

现在,你只需简单地称呼它:

NodeBase root= getRoot();
root.accept(new SearchVisitor(getSearchId()));

答案 2 :(得分:1)

看起来这个问题有两个部分 - 类层次结构的分解和搜索算法的实现。

在Java世界中,分解问题有两种可能的解决方案:

  • 面向对象的分解,具有本地特性,
  • 使用instanceoftype casting键入检查分解。

函数式语言(包括Scala)提供模式匹配,这是实现类型检查分解的更好方法。

由于需要使用数据结构(树),其中元素(节点)可以是不同类型,因此分解的性质绝对不是本地的。因此,第二种方法确实是唯一的选择。

搜索本身可以使用例如二进制搜索树算法来实现。这种树需要根据您的数据构建,其中决定放置某个节点的位置应该取决于实际的搜索标准。基本上,这意味着您需要拥有与不同搜索条件一样多的树,这实际上是构建索引的一种方式。数据库引擎使用比二叉搜索树更复杂的结构。例如,red-black trees,但这个想法非常相似。

BTW二叉搜索树具有同质性。例如,如果搜索属于Employee Department,则搜索树将仅包含与Employee个实例关联的节点。这消除了分解问题。