-1

UPDATE

Given a set of reviews provided by the customers for different hotels and a string containing “Good Words”, you need to sort the reviews in descending order according to their “Goodness Value” (Higher goodness value first). We define the “Goodness Value” of a string as the number of “Good Words” in that string.

Note: Sorting should be stable. If review i and review j have the same “Goodness Value” then their original order would be preserved.

Issue Using priority queue to arrange above order is not being stable as while polling heap arrays are not returning in same order for same values.

Meanwhile, I have checked this blog: https://lemire.me/blog/2017/03/13/stable-priority-queues/.

Is there any better way for stable priority queue ordering? or any other better DS? One way I can think of is putting it in arrays and sorting it. Treemap can't be much useful as key with duplicates entries or similar count multiple words are involved.

Question: https://www.interviewbit.com/problems/hotel-reviews/

This works

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

Output: ans = [2, 0, 1]

Here, sorted reviews are ["cool_wifi_speed", "water_is_cool", "cold_ice_drink"]

This test case doesn't work.

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;
  }

}
srs
  • 330
  • 1
  • 11
  • 1
    Your question is unclear and contains a lot of unnecessary code. Please create a [mcve]: put the 3 items in a PriorityQueue, take them out, check if the order is correct or not, take it from there. – assylias Feb 14 '18 at 15:16
  • You ALWAYS need to @Override hashCode() when you override equals(), so that two equal objects always have the same hash. Maybe that's the issue. Otherwise, the original problem is a bit ambiguous to me. Not sure why "water_is_cool" comes before "cold_ice_drink". – GregT Feb 14 '18 at 15:17
  • 3
    @GregT That is not 100% correct. By -convention-, you should override both. But if you've got no intention using your objects in a hash structure, there's no need to prepare the objects for that either. AND to add, a Priority Queue is not a hash structure, so hashCode() should not be an issue. hashCode() is overridden for speed purposes. It still works without overriding it. – Oskarzito Feb 14 '18 at 15:36
  • @Oskarzito of course the compiler won't complain, so you can technically do it, but you may introduce a lot of bugs inadvertently. More reference: https://stackoverflow.com/questions/2265503/why-do-i-need-to-override-the-equals-and-hashcode-methods-in-java – GregT Feb 14 '18 at 15:40
  • @GregT Yes, that was a really good and interesting reference! But it just pointed out that the result of the hash function hashes to different locations, which is true if there's no overriding. But it still works without, even tho collision would occur (and be handled) in unnecessary situations etc. So, as mentioned, if there's no need to use the objects as keys in a hash structure, it won't affect anything by not overriding it. – Oskarzito Feb 14 '18 at 15:47
  • @Oskarzito Well, if you work as a developer, you cannot rely on just you editing the code in its lifetime. So "if you've got no intention" is not a good argument here. Other than that, it's just a matter of opinion. I think there is a good reason experienced developers strongly recommend this. But if you don't believe this, you can also learn by your own mistakes. – GregT Feb 14 '18 at 15:59
  • @GregT In my case, "you" is referred to anyone who's using the code. Of course, if there are plenty of developers using it, yes, it should probably be done. That's why it's recommended. But you said: "You ALWAYS need to", which is not true. Thereof "if you've got no intention" works. Far from all objects will be used as keys, but might have use of equals(). Therefore, it is not necessary. I never said I didn't believe it. Besides, you just changed your point of view from "ALWAYS" to "strongly recommend this" which was my point from the beginning. – Oskarzito Feb 14 '18 at 20:07
  • Hi, guys thanks for all the comments. @assylias the full question link is at the top if you've missed the blue color link there. 'S' and 'R' are 2 inputs to the program. 'S' has some business key words and 'R' (review) has to be sorted in the manner of # of 'count' of 'R' words in 'S'. The higher the count it is positioned at first. I am using 'Trie' for quick search of 'R' words and 'priority queue' for arranging S words. I go with Oskar and certainly don't understand the need to use '#hashcode' here as there is no bucket formation here and no internal functions are also using #hashcode. – srs Feb 15 '18 at 05:52
  • As the sample input at top since `R[2] = "cool_wifi_speed"` has 2 words of `S` i.e. `cool` and `wifi` it comes first, But the issue is if words have same count priority queue does n't order them as per a stable sorting. `Ex: A : "a_b_c " B : [ "a_b", "b_c", "a_c" ]` Precisely, how to make priority queue orders stable in case of equal counts. And I certainly don't know the reason to downvote this question – srs Feb 15 '18 at 05:58
  • @srs why do you need a queue? Just sort your data with a suitable method https://en.m.wikipedia.org/wiki/Category:Stable_sorts – algrid Feb 15 '18 at 07:31
  • yeah, I did it using array, but just wanted to go in the direction of how to solve it using priority queue or any other smart priority based DS – srs Feb 15 '18 at 12:58

2 Answers2

2

As others have pointed out, you can't depend on PriorityQueue for a stable sort. The reason is that PriorityQueue is implemented with a binary heap, and binary heap does not produce a stable ordering.

But you can use PriorityQueue in your case. You want to sort by score, with equal scores being returned in order by their pos values. So if you have:

pos   score
 1      1
 2      3
 3      2
 4      1

Then you want the result to be:

pos   score
 2      3
 3      2
 1      1
 4      1

All you have to do is modify your compareTo method so that if score is equal, it compares pos, like this:

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

In general, it's a bad idea to use this.score - r.score to do a comparison. It works for positive numbers, but if you mix positive and negative numbers, integer overflow can produce errors. See my blog post, Subtraction is not the same as comparison for the details.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • I hope you meant @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; }` This solves it like a charm. Thanks – srs Feb 16 '18 at 07:19
  • @srs If that's the order you want the positions in, sure. – Jim Mischel Feb 16 '18 at 15:22
  • order is as per the example result you have described above – srs Feb 21 '18 at 11:30
0

Why PriorityQueue is not working as a stable sorting this case?

Because PriorityQueue does not provide a stable sort. See the Javadoc.

EDIT Now that you have completely changed your question:

Best practice ...

There is only one practice. Add an insertion-order field to your object and use it as a minor key.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 1
    That does not address the elephant in the room here. The question was meant to be understood as how the use of priority queue in the above context can result in stable sorting. That would indeed be some direction in the sense of solution of the above question. – srs Feb 15 '18 at 12:57
  • @srs The article you cited has the answer: "Just add some kind of counter recording the insertion order, and when you insert elements in the binary heap, just use the insertion order as to differentiate elements" – GregT Feb 15 '18 at 14:02
  • @srs As the quotation from your text makes clear, it addresses the question, or rather the fallacy, expressed in your title. If that isn't your real question, fix your title. – user207421 Feb 15 '18 at 21:50