搜索多树数据结构的最有效方法

时间:2013-06-25 05:18:42

标签: java search optimization arraylist

我想知道是否有一种更有效的方式来搜索多树而不是我一直在做的事情。我最近为项目构建了一个多树数据结构,如下所示。 Multi-Tree data structure using ArrayLists

洞穴将聚会列入阵列列表。不同的政党会拥有不同的生物。不同的生物会持有不同的物品。

我需要一种方法来搜索整个树,以便将对象与其拥有的属性(如索引)相匹配。这是我的程序片段,它搜索每个ArrayList以查看派对,生物,宝藏或工件是否与int I调用索引匹配。

修改(解释我的代码) Party,Creature,Treasure和Artifact类都具有索引,名称,类型等属性。多树将Cave设置为根音符。 Cave有一个ArrayList,可以包含许多Party对象。每个Party都有一个ArrayList,可以包含许多Creature对象。每个生物都有两个数组列表,一个用于保存Artifact对象,另一个用于Treasure对象。

下面我正在搜索哪个派对,生物,工件或宝藏包含我要搜索的特定索引。我通过迭代派对来实现这一点,我在每个派对中查看这些生物,并且每个生物都会查看文物和宝物。这就是为什么我在for循环中有这么多for循环的原因:(。

case 0 :
            int index =                 Integer.parseInt( stat );
            for ( Party p : SorcerersCave.theCave.parties ) {
                if ( index == p.getIndex()) {
                    generateInterface.theGame.printOutput( "\t" + p );
                    break;
                } else {
                    for ( Creature c : p.members ){
                        if ( index == c.getIndex() ){
                            generateInterface.theGame.printOutput( "\t" + c );
                            break;
                        } else {
                            for ( Treasure t : c.inventory ){
                                if ( index == t.getIndex() ){
                                    generateInterface.theGame.printOutput( "\t" + t );
                                    break;
                                }
                            }
                            for ( Artifact a : c.artifacts ){
                                if ( index == a.getIndex() ){
                                    generateInterface.theGame.printOutput( "\t" + a );
                                    break;
                                }
                            }
                        }
                    }
                }
            }

我觉得这段代码太复杂了,很难理解。代码可以工作,但它在我看起来非常好看的代码上是一个丑陋的污点。我一直在四处寻找更好的方法来做到这一点,甚至是改善它的方法。

注意*项目要求禁止我们将每个对象放在同一个ArrayList中。

7 个答案:

答案 0 :(得分:2)

您的类可以实现一个看起来像这样的公共接口SearchableByIndex,它将由树中的每个类实现:

public interface SearchableByIndex {
    public SearchableByIndex searchByIndex(int index);
    public int getIndex();
}

然后CavePartyCreature需要此代码:

public SearchableByIndex searchByIndex(int index) {
    if (getIndex() == index) {
        return this;
    } else {
        for (Party party : parties) {    // or Creature creature : members etc.
            SearchableByIndex found = party.searchByIndex(index);
            if (found != null) {
                return found;
            }
        }
    }
    return null;
}

宝藏与神器的版本:

public SearchableByIndex searchByIndex(int index) {
    return (getIndex() == index) ? this : null;
}

而不是你的嵌套循环,你有

case 0:
    int index = Integer.parseInt(stat);
    SearchableByIndex foundItem = SorcerersCave.theCave.searchByIndex(index);
    if (foundItem != null) {
        generateInterface.theGame.printOutput("\t" + foundItem);
    } else {
        // print out that the item was not found
    }
    break;

虽然每个类的searchByIndex()方法看似重复,但这是OOP的方法。这样,每个班级都会负责搜索其所有内容。在你的情况下,所有类都将具有非常相似的代码(唯一的变化将是for循环的行),但你可以认为这是一个意外 - 任何类都可以包含它想要的任何数据,没有其他类应该关心关于它如何存储数据。

将来,如果您的某个课程中包含更多可搜索的数据(TreasureArtifactHostage?),或者您想要更改其数据结构(考虑HashMap,如果可以!),您只需更改特定类中的searchByIndex()方法。

此外,只有包含数据的类应该知道 它包含它的方式。你现在拥有它的方式,其他一些调用搜索的类确切知道数据在每个类中的存储方式 - 这不应该。如果一个类发生更改,则会破坏其他类的内部代码。例如,Cave类应在其文档中声明它包含例如不同的Party个实例,如果您按searchForIndex()搜索它们,它可以发出一些实例。

答案 1 :(得分:2)

  

项目要求禁止我们将每个对象放在同一个ArrayList中。

(据推测,HashMapTreeMap也被禁止......)

如果没有某种辅助数据结构将index值映射到它们引用的对象,则您有无选择但要进行树遍历。所以平均而言,如果有O(N)个对象,那么“查找对象”代码将为N ...具有相当大的比例常数。

(据推测,无论谁对问题施加约束都不关心表现。你应该采取同样的态度......特别是考虑到你的评论中提出的背景。)

您可以通过几种方式改进代码,使其更具可读性(并且最有可能)更高效。

  1. 使用“break to label”,以便内部循环在被击中时退出到外部循环的末尾。 (假设这就是你想要的。)

  2. 每次在容器层次结构中查找元素时,使用临时变量来避免评估多个方法调用。 更新:看起来您刚刚重写了代码来执行此操作...


  3.   

    ...项目要求规定每个对象需要在多树数据结构中实例化,如我发布的图表所示。

    但他们是否明确禁止二级数据结构?或者您只是假设您不被允许这样做?

答案 2 :(得分:1)

我会改为使用for-each循环:

int index = Integer.parseInt(stat);
for(Party party : parties){
    if( index == party.members.get( c ).getIndex() ){
        generateInterface.theGame.printOutput( "\t" + party.summary );
        break;
    } else {
        for(Creature creature : party.Creatures){
            // etc etc.....
        }
    }
}

这会使代码更容易阅读。它的缺点是你没有在循环中内置计数器。

编辑:本文可能会澄清: https://blogs.oracle.com/CoreJavaTechTips/entry/using_enhanced_for_loops_with

答案 3 :(得分:1)

这是一个非常奇怪的约束集,但是根据树遍历的精神,根本不需要修改原始数据结构,为什么不将朴素递归遍历函数写为一​​组重载方法呢?它可能看起来像这样:

public Object search(int index, Cave c) {
  if (c.id == index) return c;

  for (Party p : c.parties) {
    Object result = search(index, p);
    if (result != null) 
      return result;
  }

  return null;
}

public Object search(int index, Party p) {
  ...
}

等等。

答案 4 :(得分:0)

你能使用更规则的结构吗?沿着这些方向的东西(我正在使用伪代码):

interface INode { ... stuff kept in nodes is described here ... }
interface ITree extends INode { bool HasId(int id); ITree[] Children; }

INode SearchTreeForId(ITree[] children, int id) {
  for each (child in children) {
    match = (child.HasId(id) ? child : SearchTreeForId(child.Children));
    if (match != null) return match;
  }
  return null;
}

// Then...
{
  node = SearchTreeForId(SorcerersCave.theCave.Parties, id);
  if (node != null) generateInterface(...);
}

答案 5 :(得分:0)

使用辅助方法进行深度优先搜索。

private Interface IndexObject
{
    // Interface will be used in dfsList
    public int getIndex();
}

private Interface ListObject extends IndexObject
{
    // For classes with sublists
    public ArrayList<IndexObject> getList();
}

public void search(int index) {
    Object found = dfsList(parties, index);
}

// Helper method
public Object dfsList(ArrayList<IndexObject> aList, int index) {
    for (IndexObject obj : aList) {
        if (obj.getIndex() == index) {
            return obj;
        }
        else if (obj instanceof ListObject) {
            IndexObject found = dfsList(obj.getList(), index);
            if (found != null) {
                return found;
            }
        }
    }
    return null;
}

答案 6 :(得分:0)

因为您的入口点是Cave对象,所以可以想到制作一个像主键一样的唯一字符串来表示整个Cave对象(Cave,Part,Creatures,Treasures,Artifacts),例如用于连接您想要的那些字段搜索例如,如果你想通过索引3搜索工件,你可以创建一个像

这样的字符串
C-P-C-T/A 

其中每个字母代表'this'类中的唯一索引,所以在这种情况下它可能就像

3-4-32-A3 

其中3代表第3洞,4代表第4方....直到A3代表神器3。

希望你找到比这更好的解决方案,如果请分享。