用于对树中的元素进行排名的算法/数据结构

时间:2010-12-14 00:08:59

标签: java algorithm sorting data-structures collections

这就是我所拥有的:具有任意数量级别的树。我需要一种方法来对每个级别的每个级别的所有节点进行排名。如果不清楚的话,那就说我的第一个级别就是世界。我的第二级是大陆。我的第三级是国家。我的第四级是城市。每个国家都有一个城市列表,按人口排序。每个大陆都有按人口排名的国家名单。每个大陆也有一份按人口排名的城市名单。等等。

我想象的算法是非常简单的递归,但我不确定跟踪这些列表的最佳数据结构是什么。每个级别都不知道它有多少子级别,所以我不能声明任意数量的列表。

有什么想法吗?

以下是一些示例代码:

public void calcStats()
    {
        initWorldRanks();//clears ranks for the world
        for(Entity continent:theWorld.getChildren())
        {
            initContinentRanks();//clears ranks for the continent
            for(Entity country:continent.getChildren())
            {
                initCountryRanks();//clears ranks for the country
                for(Entity city:country.getChildren())
                {
                                    //Assume that add preserves sorted order.  Sorting is easy.  The tricky part is that each entity needs to be added to its ancestors.  I don't want to have fixed data structures
                    worldCityRanks.add(city);
                    continentCityRanks.add(city);
                    countryCityRanks.add(city);
                }
                worldCountryRanks.add(country);
                            continentCountryRanks.add(country);
            }
            worldContinentRanks.add(continent);
        }

一切都正确排名,但这限制了我一个明确的4级结构。

1 个答案:

答案 0 :(得分:1)

关键是你不想通过遍历整个子树来重新计算每个节点的计数。缓存每个节点的总计数。然后,每个节点只需要从子节点收集值来计算它自己的总数(它也应该缓存)。

您没有说这些节点是否可变。如果它们是不可变的,那么很简单:在构造时添加所有子节点时构建节点的总数。

如果它们是可变的,您可以让每个节点在其计数发生变化时告诉其父节点。父母可以更新自己的计数并告诉其父级,然后在树上。这样可以更新计数O(树的深度)或大致O(logn)(取决于树的平衡程度)。

对于实际排序每个节点的孩子做你通常做的事情:使用ArrayList并对其进行排序,或者使用某种维护排序顺序的排序集合(例如:TreeSet,尽管make确定你在具有相同人口的元素之间徘徊。重要的是,在比较时,你只会看到你孩子的直接价值(即:缓存的金额),而不是你的间接后代。

更新

根据您对问题的更新,您的一个问题是您可以获得在不同级别添加内容的单独方法。即:worldCityRanks.addcontinentCityRanks.addcountryCityRanks.add等。您应该使用以深度为参数的单个方法替换所有这些。例如:

// Probably in your Entity class
public void addDescendant(int distance, Entity descendant) {
  // this replaces worldCityRanks.add, continentCityRanks.add,
  // countryCityRanks.add, etc.
}

然后你没有为你的后代集合提供4个字段,而是拥有一个集合(可能是ArrayList)来保存它们。你可以根据需要进行扩展。

另一个问题是你有这些硬编码的嵌套for循环。要处理任意(在合理范围内)深度,最简单的方法是使用递归。例如:

public void calcStats() {
  theWorld.initAllRanks();
  List<Entity> ancestors = new ArrayList<Entity>();
  theWorld.accumulateAllRanks(ancestors);
}

class Entity ... {
  ...

  void initAllRanks() {
    initRanks();
    for(Entity child: getChildren()) {
      child.initAllRanks();
    }
  }

  void accumulateAllRanks(List<Entity> ancestors) {
    int distance = ancestors.size();
    for(Entity ancestor: ancestors) {
      distance--;
      ancestor.addDescendant(distance, this);
    }
    ancestors.add(this); // push this
    for(Entity child: getChildren()) {
      child.accumulateAllRanks(ancestors);
    }
    ancestors.remove(ancestors.size() - 1); // pop this
  }

这假设您确实想要存储每个级别的排名(这是您的代码示例所暗示的)。这种方法使查找速度更快,但它可以使更新速度变慢,并且与其他方法相比也会消耗更多内存。特别是,您可以只维护全局排名列表,然后在查询时过滤这些列表。同样,这使得更新更快并且消耗更少的内存,但使查询比您当前使用的方法慢。