Efficient way to find min value in Map

为君一笑 提交于 2019-12-10 13:16:14

问题


I am trying to find key with minimum value in Map shown below.

 Map<Node, Integer> freeMap = new TreeMap<>();
 Node minNode = null;
        for (Map.Entry<Node, Integer> entry : freeMap.entrySet()) {
            if (minNode == null) {
                minNode = entry.getKey();
            } else {
                if (entry.getValue() < freeMap.get(minNode)) {
                    minNode = entry.getKey();
                }
            }
        }

Firstly, Is there a straight forward way to find key with minimum value than using foreach loop. Secondly, can you suggest some alternate data structure approach which can be used to store a Node object and an associated Integer value, so I can fetch entry with minimum value in constant time O(1).


回答1:


If your goal is to improve time complexity, there's really only one possible change, from O(n log n) to O(n):

 Map<Node, Integer> freeMap = new TreeMap<>();
 Map.Entry<Node, Integer> minEntry = null;
 for (Map.Entry<Node, Integer> entry : freeMap.entrySet()) {
   if (minEntry == null || entry.getValue() < minEntry.getValue()) {
      minEntry = entry;
   }
 }
 Node minNode = minEntry.getKey();



回答2:


I suspect that Integer values are not unique in your system.

If this is the case, I suggest you use TreeMultimap from guava library, and use Integer value as a key.

TreeMultimap<Integer, Node> freeMap = new TreeMultimap<>();
Node minNode =
  freeMap.isEmpty() 
    ? null
    : freeMap.entries().iterator().next().getValue();



回答3:


Minor improvement not a whole bunch :

Map<Node, Integer> freeMap = new TreeMap<Node, Integer>();
Node minNode = freeMap.isEmpty() ? null : (Node) freeMap.entrySet().iterator().next();
for (Map.Entry<Node, Integer> entry : freeMap.entrySet()) {
    if (entry.getValue() < freeMap.get(minNode)) {
        minNode = entry.getKey();
    }
}

Got the if check out of the loop.




回答4:


Data Structure for O(1) Min For an alternative data structure, how about a Priority Queue.

You can either use a custom Comparator or have your data type implement Comparable.

From the javadoc:

Implementation note: this implementation provides O(log(n)) time for the enqueing and dequeing methods (offer, poll, remove() and add); linear time for the remove(Object) and contains(Object) methods; and constant time for the retrieval methods (peek, element, and size).

Data Structure for O(1) Min and amortized O(1) find

If you want both efficient min and efficient find and you control access to the data structure (otherwise what is the point of the question?) you can just roll out your own by extending Java's HashMap to keep track of the minimum element.

You will have to override the put, putAll and remove methods. In each case, you can just call the super class method (e.g. super.put(key, value)) and then update the minimum element, which is kept as an instance member of your newly defined class.

Note that this increases the remove time to (O(N)) (since you will have to update the minimum value).




回答5:


You can define your own Comparator and use Collections.min. Example:

Comparator<Entry<Node, Integer>> customComparator = new Comparator<>() {
        @Override
        public int compare(Entry<Node, Integer> o1, Entry<Node, Integer> o2){
            return (int)(o1.getValue() - o2.getValue());
        }

        @Override
        public boolean equals(Object obj) {
            return false;
        }
    };

Entry<Node, Integer> minVal = Collections.min(freeMap.entrySet(), customComparator);

Hope this helps :)




回答6:


The keys for a concise, efficient and elegant solution here are the Collections#min method and the Map.Entry#comparingByValue method

The first method can be applied to the entrySet of the map, and the second one provides a Comparator that compares map Entry objects by their value. So the solution is a one-liner, and you can either obtain the entry or the key directly, as shown in this example:

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

public class KeyWithMinValue
{
    public static void main(String[] args)
    {
        Map<String, Integer> map = new LinkedHashMap<String, Integer>();

        map.put("Zero", 0);
        map.put("One", 1);
        map.put("Two", 2);
        map.put("Three", 3);
        map.put("Four", 4);

        // Obtain the entry with the minimum value:
        Entry<String, Integer> entryWithMinValue = Collections.min(
            map.entrySet(), Entry.comparingByValue());
        System.out.println(entryWithMinValue);

        // Or directly obtain the key, if you only need that:
        String keyWithMinValue = Collections.min(
            map.entrySet(), Entry.comparingByValue()).getKey();
        System.out.println(keyWithMinValue);
    }
}


来源:https://stackoverflow.com/questions/26513747/efficient-way-to-find-min-value-in-map

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!