【问题标题】:Best practice in preserving insertion order with priority queue in case of equality for stable sorting在稳定排序相等的情况下使用优先队列保留插入顺序的最佳实践
【发布时间】:2018-02-14 15:09:25
【问题描述】:

更新

给定客户为不同酒店提供的一组评论和一个包含“Good Words”的字符串,您需要根据评论的“Goodness Value”降序排序(较高的goodness value首先)。我们将字符串的“Goodness Value”定义为该字符串中“Good Words”的数量。

注意:排序应该是稳定的。如果评论 i 和评论 j 具有相同的“Goodness Value”,那么它们的原始顺序将被保留。

问题 使用priority queue 排列上述顺序并不稳定,因为轮询堆数组不会以相同的顺序返回相同的值。

同时,我查看了这个博客:https://lemire.me/blog/2017/03/13/stable-priority-queues/

有没有更好的方法来稳定优先级队列排序?或任何其他更好的DS?我能想到的一种方法是将其放入arrayssorting 中。 Treemap 不能像 key 那样有用,因为涉及重复条目或类似计数的多个单词。

问题https://www.interviewbit.com/problems/hotel-reviews/

这行得通

输入: S = "cool_ice_wifi" R = ["water_is_cool", "cold_ice_drink", "cool_wifi_speed"]

输出: ans = [2, 0, 1]

在这里,排序的评论是 ["cool_wifi_speed", "water_is_cool", "cold_ice_drink"]

这个测试用例不起作用。

A:“a_b_c”
B:[“a_b”,“b_c”,“a_c”]

public class Solution {

  private static final int ALPHA_SIZE = 26;

  public static class Reviews implements Comparable<Reviews> {
    private int pos;
    private int score;

    @Override
    public boolean equals(Object o) {
        if(this == o) return true;
        if(o == null || this.getClass()!=o.getClass()) return false;

        Reviews r = (Reviews)o;
        if(r.score!=this.score) return false;

        return true;
    }

    @Override
    public int compareTo(Reviews r) {
        return this.score - r.score;
    }
  }

  public static class TrieNode {
    TrieNode[] letter;
    boolean isTail;

    public TrieNode() {
        isTail = false;
        letter = new TrieNode[ALPHA_SIZE];
        for(int i = 0; i < ALPHA_SIZE; i++) {
            letter[i] = null;
        }
    }

  }

  static TrieNode root; 

  public static void add(String word) {
    TrieNode curr = root;
    for(char ch : word.toCharArray()) {
        if(curr.letter[ch-'a']==null) curr.letter[ch-'a'] = new TrieNode();
        curr = curr.letter[ch-'a'];
    }
    curr.isTail = true;
  }

  public void addAll(String[] words) {
    for(String word : words) {
        this.add(word);
    }   
  }

  public static boolean contains(String word) {
    TrieNode curr = root;
    for(char ch : word.toCharArray()) {
        if(curr.letter[ch-'a']!=null) {
            curr = curr.letter[ch-'a'];
        } else {
            return false;
        }
    }
    return curr.isTail;
  }

  public ArrayList<Integer> solve(String A, ArrayList<String> B) {

    root = new TrieNode();
    String[] words = A.split("_");
    addAll(words);
    ArrayList<Integer> res = new ArrayList<>();

    Reviews cur;
    Queue<Reviews> q = new PriorityQueue<>(Collections.reverseOrder());
    for(int i = 0; i < B.size(); i++) {
        cur = new Reviews();
        cur.score = countGW(B.get(i));
        cur.pos = i;
        q.add(cur);
    }

    while(!q.isEmpty()) {
        cur = q.poll();
        res.add(cur.pos);
    }

    return res;
  }

  public int countGW(String x) {
    String[] xs = x.split("_");
    int res = 0;

    for(String y : xs) {
        if(contains(y)) {
            res++;
        }
    }

    return res;
  }

}

【问题讨论】:

  • 你的问题不清楚,包含很多不必要的代码。请创建一个minimal reproducible example:将3个项目放入PriorityQueue,取出,检查顺序是否正确,从那里取出。
  • 当你覆盖 equals() 时,你总是需要 @Override hashCode(),这样两个相等的对象总是有相同的哈希值。也许这就是问题所在。否则,原来的问题对我来说有点模棱两可。不知道为什么“water_is_cool”出现在“cold_ice_drink”之前。
  • @GregT 这不是 100% 正确的。通过-约定-,您应该覆盖两者。但是,如果您无意在散列结构中使用您的对象,则也无需为此准备对象。并且要补充一点,优先队列不是哈希结构,因此 hashCode() 应该不是问题。 hashCode() 被覆盖以提高速度。它仍然可以在不覆盖它的情况下工作。
  • @Oskarzito 当然编译器不会抱怨,所以技术上你可以做到,但你可能会在不经意间引入很多错误。更多参考:stackoverflow.com/questions/2265503/…
  • @GregT 是的,这是一个非常好的和有趣的参考!但它只是指出散列函数的结果散列到不同的位置,如果没有覆盖,这是真的。但它仍然可以在没有的情况下工作,即使在不必要的情况下会发生(并被处理)等冲突。所以,如前所述,如果不需要将对象用作哈希结构中的键,它不会通过不覆盖而影响任何事情它。

标签: java sorting data-structures priority-queue trie


【解决方案1】:

正如其他人指出的那样,您不能依赖PriorityQueue 来进行稳定排序。原因是PriorityQueue是用二叉堆实现的,二叉堆不会产生稳定的排序。

但你可以在你的情况下使用PriorityQueue。您想按score 排序,并按pos 值的顺序返回相等的分数。所以如果你有:

pos   score
 1      1
 2      3
 3      2
 4      1

那么你想要的结果是:

pos   score
 2      3
 3      2
 1      1
 4      1

您所要做的就是修改您的compareTo 方法,以便如果score 相等,它会比较pos,如下所示:

@Override
public int compareTo(Reviews r) {
    int result = this.score.compareTo(r.score);
    if (result != 0) return result;
    result = this.pos.compareTo(r.pos);
}

一般来说,使用this.score - r.score 进行比较是个坏主意。它适用于正数,但如果混合正数和负数,整数溢出会产生错误。详情请参阅我的博文Subtraction is not the same as comparison

【讨论】:

  • 我希望你的意思是@Override public int compareTo(Reviews r) { int result = this.score.compareTo(r.score); if (result != 0) return result; result = r.pos.compareTo(this.pos); return result; } 这就像一个魅力一样解决了它。谢谢
  • @srs 如果这是您想要的职位顺序,当然。
  • 顺序是根据您上面描述的示例结果
【解决方案2】:

为什么PriorityQueue 在这种情况下不能作为稳定的排序方式?

因为PriorityQueue 不提供稳定的排序。请参阅 Javadoc。

编辑现在您已经完全改变了您的问题:

最佳实践 ...

只有一种做法。向您的对象添加一个插入顺序字段并将其用作次要键。

【讨论】:

  • 这里没有提到房间里的大象。该问题旨在理解为在上述上下文中使用优先级队列如何导致稳定排序。这确实是解决上述问题的某种方向。
  • @srs 您引用的文章有答案:“只需添加某种记录插入顺序的计数器,当您在二进制堆中插入元素时,只需使用插入顺序来区分元素”
  • @srs 正如您的文字中的引文所表明的那样,它解决了您的标题中表达的问题,或者更确切地说是谬误。如果这不是您真正的问题,请修正您的标题。
猜你喜欢
  • 2018-08-19
  • 2018-03-20
  • 1970-01-01
  • 2011-10-18
  • 2011-05-20
  • 1970-01-01
  • 1970-01-01
  • 2012-07-15
  • 2012-11-01
相关资源
最近更新 更多