自定义数据结构(元素+重量)

时间:2014-06-26 01:10:19

标签: java data-structures heap

我应该使用哪种数据结构来保持按给定重量排序的元素?我需要在集合中添加元素,其中每个元素生成一个特定的权重,但这个权重不包含在元素本身内部(既不计算);它由元素之外的其他人计算。此外,不需要存储权重(但如果需要可以存储),它只是一个插入参数,可以将元素放在正确的位置。

我的具体用例是对音乐曲目进行排序。我有一个轨道列表和一个适用于每个轨道的规则列表。我遍历每个轨道,然后规则列表生成一个"得分"鉴于目前的轨道。我想将曲目添加到"集合"鉴于生成的分数,我可以选择得分最高的第一首曲目。

我无法看到在Java中使用哪种数据结构。我是否必须自己实施一个新的?我最好的猜测是一种堆数据结构,但我无法弄清楚哪一个具体。

编辑: 重量仅在开始时计算一次。我不能提供一个"比较器"因为它会比较曲目,我无法在" compareTo方法"上计算他们的分数。 该算法可能如下所示:

 public Track determineNext() {
     MyStructure<Track> trackScores;
        for (Track track : tracks) {
            int score = 0;

            for (Rule rule : rules) {
                score += rule.applyScoreOn(track);
            }

            trackScores.add(track, score);
        }
     return trackScores.first();
 }

我希望你明白这一点。

1 个答案:

答案 0 :(得分:1)

我认为PriorityQueue正是您所需要的:

  

优先级队列的元素按其自然顺序排序,或者由队列构造时提供的比较器排序,具体取决于使用的构造函数。

在这种情况下,自然排序将按分数进行。容易。

关于你的观点:

  

我无法提供一个&#34;比较器&#34;因为它会比较曲目,我无法在&#34; compareTo方法&#34;上计算他们的分数。

这里的关键是您不能使用compareTo()方法来计算分数。反之亦然。您在compareTo()的实施中使用得分。


您可以通过以下几种方式实现此目标:

  1. 制作Track implements Comparable<Track>,并实施compareTo(Track t)比较您得分,所以得分更高 - &gt; &#34;较大的&#34;宾语。 PriorityQueue将处理其余的事情。
  2. 实施与Comparator<Track>基本相同的compareTo()选项1。
  3. 如果您可能稍后在程序中使用不同的条件进行排序,则选项2会更好,因为您不会仅仅按照分数对Track进行排序。

    理想情况下,对于这两个选项,您可以将分数存储在Track内,因为分数只需要计算一次;但是,您必须确保在插入PriorityQueue之前设置此分数。但这不应该成为一个问题。

    或者,如果您知道得分不会随时间变化,可以在比较方法中即时计算得分;但是,如果必须进行大量的比较,这会随着时间的推移而变得有些昂贵。如果您知道得分不会改变,我不会推荐这个。


    或者,如果Track个对象对其得分一无所知(这是有道理的),您可以使用Map Track作为关键字并将得分作为关联的值Track得分:

    HashMap<Track, Integer> scoreMap = new HashMap<>();
    for (Track track : tracks) { // Copied from question, slightly altered
        int score = 0;
    
        for (Rule rule : rules) {
            score += rule.applyScoreOn(track);
        }
    
        scoreMap.put(track, Integer.valueOf(score));
    

    现在您可以使用从此地图中提取分数的比较器

    public class TrackComparator implements Comparator<Track> {
        private final Map<Track, Integer> scores;
    
        public TrackComparator(Map<Track, Integer> scores) {
            this.scores = scores;
        }
    
        @Override
        public int compare(Track t1, Track t2) {
            // Check to see if tracks are in map if you didn't pre-fill map
            // Get scores, compare, return
        }
    }
    

    但有几点需要注意:

    • Map通常要求您定义其他方法(hashCode()equals() HashMap以及TreeMap的某种形式的比较器< / LI>
    • 地图的密钥不应该是可变的,但对于Track个对象,我真的不能确定这是一个问题
    • 你得到另一个集合来吃掉记忆。但是,如果你的分数在[-128,127]范围内,那不应该是一个问题。也可以通过VM标志来扩展此范围。我不会过多担心内存消耗,除非你有一些疯狂的曲目或几个不同的地图浮动。
    • 只是在外部更改地图的值不会对优先级队列重新排序。你必须删除()并添加()对象的&#34;得分&#34;已更改为移动到队列中的新位置。

    我不太确定Map解决方案是最佳解决方案,但我认为远非最差,并且本身应该相当不错。

    帮助解决内存问题的一个可能的变化是将Map置于Comparator内,并让比较器本身根据需要计算分数,如果轨道不是,则将它们添加到地图中。已经存在了。这取决于你是否关心某些分数被冻结&#34;比其他人晚。这可能会对性能造成不利影响,但这是一个很大的猜测。