0

I have a HashMap and have to print the N-th highest value in the HashMap.

I have managed to get the highest value.

I have sorted the HashMap first so that if there are two keys with the same value, then I get the key that comes first alphabetically.

But I still don't know how to get the key for nth highest value?

public void(HashMap map, int n) {
    Map<String, Integer> sortedmap = new TreeMap<>(map);
    Map.Entry<String, Integer> maxEntry = null;
    for (Map.Entry<String, Integer> entry : sortedmap.entrySet()) {
        if (maxEntry == null || entry.getValue().compareTo(maxEntry.getValue()) > 0) {
            maxEntry = entry;
        }
    }
    System.out.println(maxEntry.getKey());
}
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
student
  • 39
  • 1
  • 7
  • 1
    What is the `3rd` highest value of `8,8,7,7,6,6,5,4,3,2,1`? It makes sense for it to be `6` but you didn't clarify that in your question. 7 would be the value in the third location which is different. – WJS Apr 10 '22 at 02:21
  • 1
    Yes correct, The third highest value will be 6 and I have to print the key for that value(Duplicates will be ignored). For eg. the Hashmap is : "peter" : 40, "mike" : 90, "sam": 60, "john":90, "jimmy" : 32, "Alex":60. In this case 2nd highest value will be 60 and there are two keys i.e. Sam and Alex having the same value. In this case we have to sort the keys alphabetically and return only the first one i.e. "Alex": 60 – student Apr 10 '22 at 14:37
  • I included your data in my answer below. If you are unfamiliar with sorting stability you may also want to check out the sorting algorithms to see what is meant by a `stable sort`. – WJS Apr 10 '22 at 16:08
  • @student This information must be included in your question, not posted as the comment – Alexander Ivanchenko Apr 11 '22 at 00:21

3 Answers3

0

This problem doesn't require sorting all the given data. It will cause a huge overhead if n is close to 1, in which case the possible solution will run in a linear time O(n). Sorting increases time complexity to O(n*log n) (if you are not familiar with Big O notation, you might be interested in reading answers to this question). And for any n less than map size, partial sorting will be a better option.

If I understood you correctly, duplicated values need to be taken into account. For instance, for n=3 values 12,12,10,8,5 the third-largest value will be 10 (if you don't duplicate then the following solution can be simplified).

I suggest approaching this problem in the following steps:

  • Reverse the given map. So that values of the source map will become the keys, and vice versa. In the case of duplicated values, the key (value in the reversed map) that comes first alphabetically will be preserved.
  • Create a map of frequencies. So that the values of the source map will become the keys of the reversed map. Values will represent the number of occurrences for each value.
  • Flatten the values of reversed map into a list.
  • Perform a partial sorting by utilizing PriorityQueue as container for n highest values. PriorityQueue is based on the so called min heap data structure. While instantiating PriorityQueue you either need to provide a Comparator or elements of the queue has to have a natural sorting order, i.e. implement interface Comparable (which is the case for Integer). Methods element() and peek() will retrieve the smallest element from the priority queue. And the queue will contain n largest values from the given map, its smallest element will be the n-th highest value of the map.

The implementation might look like this:

public static void printKeyForNthValue(Map<String, Integer> map, int n) {
    if (n <= 0) {
        System.out.println("required element can't be found");
    }
    
    Map<Integer, String> reversedMap = getReversedMap(map);
    Map<Integer, Integer> valueToCount = getValueFrequencies(map);
    List<Integer> flattenedValues = flattenFrequencyMap(valueToCount);
    Queue<Integer> queue = new PriorityQueue<>();
    
    for (int next: flattenedValues) {
        if (queue.size() >= n) {
            queue.remove();
        }
        queue.add(next);
    }
    
    if (queue.size() < n) {
        System.out.println("required element wasn't found");
    } else {
        System.out.println("value:\t" + queue.element());
        System.out.println("key:\t" + reversedMap.get(queue.element()));
    }
}

private static Map<Integer, String> getReversedMap(Map<String, Integer> map) {
    Map<Integer, String> reversedMap = new HashMap<>();
    for (Map.Entry<String, Integer> entry: map.entrySet()) { // in case of duplicates the key the comes first alphabetically will be preserved
        reversedMap.merge(entry.getValue(), entry.getKey(),
                    (s1, s2) -> s1.compareTo(s2) < 0 ? s1 : s2);
    }
    return reversedMap;
}

private static Map<Integer, Integer> getValueFrequencies(Map<String, Integer> map) {
    Map<Integer, Integer> result = new HashMap<>();
    for (Integer next: map.values()) {
        result.merge(next, 1, Integer::sum); // the same as result.put(next, result.getOrDefault(next, 0) + 1);
    }
    return result;
}

private static List<Integer> flattenFrequencyMap(Map<Integer, Integer> valueToCount) {
    List<Integer> result = new ArrayList<>();
    for (Map.Entry<Integer, Integer> entry: valueToCount.entrySet()) {
        for (int i = 0; i < entry.getValue(); i++) {
            result.add(entry.getKey());
        }
    }
    return result;
}

Note, if you are not familiar with Java 8 method merge(), inside getReversedMap() you can replace it this with:

if (!reversedMap.containsKey(entry.getValue()) || 
      entry.getKey().compareTo(reversedMap.get(entry.getValue())) < 0) {
   reversedMap.put(entry.getValue(), entry.getKey());
}

main() - demo

public static void main(String[] args) {
    Map<String, Integer> source =
        Map.of("w", 10, "b", 12, "a", 10, "r", 12,
               "k", 3, "l", 5, "y", 3, "t", 9);

    printKeyForNthValue(source, 3);
}

Output (the third-greatest value from the set 12, 12, 10, 10, 9, 5, 3, 3)

value:  10
key:    a
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
0

Here is one way. It is presumed by Nth highest that duplicates must be ignored. Otherwise you would be asking about position in the map and not the intrinsic value as compared to others. For example, if the values are 8,8,8,7,7,5,5,3,2,1 then the 3rd highest value is 5 where the value 8 would be simply be value in the 3rd location of a descending sorted list.

  • initialize found to false and max to Integer.MAX_VALUE.
  • sort the list in reverse order based on value. Since the TreeMap is already sorted by keys and is a stable sort (see Sorting algorithms) the keys will remain in sorted order for duplicate values.
  • loop thru the list and continue checking if the current value is less than max. The key here is less than, That is what ignores the duplicates when iterating thru the list.
  • if the current value is less than max, assign to max and decrement n. Also assign the key
  • if n == 0, set found to true and break out of the loop.
  • if the loop finishes on its own, found will be false and no nth largest exists.
Map<String, Integer> map = new TreeMap<>(Map.of(
         "peter" , 40, "mike" , 90, "sam",60, "john",90, "jimmy" , 32, "Alex",60,"joan", 20, "alice", 40));

List<Entry<String,Integer>> save = new ArrayList<>(map.entrySet());

save.sort(Entry.comparingByValue(Comparator.reverseOrder()));

int max = Integer.MAX_VALUE;
boolean found = false;
String key = null;
for (Entry<String,Integer> e : save) {
    if (e.getValue() < max) {
        max = e.getValue();
        key = e.getKey();
        if (--n == 0) {
            found = true;
            break;
        }
    }
}

if (found) {
    System.out.println("Value = " + max);
    System.out.println("Key = " + key);
} else {
    System.out.println("Not found");
}

prints

Value = 60
Key = Alex

    
WJS
  • 36,363
  • 4
  • 24
  • 39
  • 1
    Thank you, this solution works. I just have to do one more thing i.e. If there are more than 1 record present for nth highest value then I have to sort the key and print the first one. Should I use TreeMap for sorting the keys and get the key for nth max value ? – student Apr 10 '22 at 00:34
  • I have to sort the keys. I got the nth highest value and suppose there are 4 keys in the HashMap that have the same nth highest value. I don't have to print all the four keys. I have to sort the key and print first one. – student Apr 10 '22 at 00:52
  • Both the key and value will be returned as required based on my interpretation of the question. – WJS Apr 10 '22 at 10:38
-1

When finding the kth highest value, you should consider using a priority queue (aka a heap) or using quick select.

A heap can be constructed in O(n) time however if you initialize it and insert n elements, it will take O(nlogn) time. After which you can pop k elements in order to get the kth highest element

Quick select is an algorithm designed for finding the nth highest element in O(n) time

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 09 '22 at 23:02