7

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).

Meena Chaudhary
  • 9,909
  • 16
  • 60
  • 94
  • 1
    The goal here is to improve time complexity of the code. – Meena Chaudhary Oct 22 '14 at 17:47
  • http://docs.oracle.com/javase/6/docs/api/java/util/SortedMap.html Maybe a SortedMap? – Chris Bolton Oct 22 '14 at 17:48
  • @ChrisBolton `SortedMap` will sort `Keys` not `values`. isn't it ? – Meena Chaudhary Oct 22 '14 at 18:09
  • Is the map already populated and will it ever change? – dkatzel Oct 22 '14 at 18:10
  • @dkatzel yes the map is populated and `value` for keys can change. – Meena Chaudhary Oct 22 '14 at 18:12
  • 1
    you can get O(1) query for minimum query - if you keep your data sorted or use a priority Queue or keep track of minimum while inserting data to Map – Mitesh Pathak Oct 22 '14 at 18:20
  • If you really need to use a Map, go with the suggestion by @LouisWasserman. If not, you should think what is the desired complexity of insertion, deletion and min(). Based on these you can choose the DS you need. A smarter heap (e.g. Fibonacci) can do _amortized_ O(1) insert and O(1) min. And see [this](http://stackoverflow.com/questions/6273833/is-there-a-standard-java-implementation-of-a-fibonacci-heap) SO post about Fibonacci Heap in Java. – paul-g Oct 22 '14 at 18:30

6 Answers6

3

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();
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 2
    No, because of this line `entry.getValue() < freeMap.get(minNode)` – paul-g Oct 22 '14 at 18:27
  • From Treemap javadocs: "This implementation provides guaranteed log(n) time cost for the containsKey, get, put and remove operations. " – paul-g Oct 22 '14 at 18:27
3

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);
    }
}
Marco13
  • 53,703
  • 9
  • 80
  • 159
0

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).

paul-g
  • 3,797
  • 2
  • 21
  • 36
  • `Priority Queue` will either store my `Node` object or its associated `value`. It can store `key-value` pair ? – Meena Chaudhary Oct 22 '14 at 17:49
  • You could store a class Pair if you are so inclined where String is the key. Then you implement `Comparable` and only compare on the Node. – paul-g Oct 22 '14 at 17:52
  • @MeenaChaudhary if the `Node` doesn't know its own value, you can put it in an object (implementing `Comparable`) that does, then put *that* in the priority queue. – Andrew Aylett Oct 22 '14 at 17:52
  • @AndrewAylett you are suggesting creating a wrapper for `Node` object and its `value` and putting that in `Priority Queue`. That can work but would prefer keeping code simple and clean. But will try if it improves performance in fetching the minimum `value` `Node`. – Meena Chaudhary Oct 22 '14 at 18:02
  • How would this improve performance at all? If you've already got a `Map`, then this isn't going to improve the asymptotics. – Louis Wasserman Oct 22 '14 at 18:12
  • This is answering the second part of the question: "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)." – paul-g Oct 22 '14 at 18:13
  • I tried wrapping `Node` and its associated `value` in a wrapper and implemented `Comparable` on `value`, so that Priority Queue can use it to sort the objects. And it does improve performance as I can `poll()` minimum value object in O(1). But now it is hard to use `contains(key)` using `Node` as key is `Node` as it contains wrapper objects not `Node` objects. – Meena Chaudhary Oct 23 '14 at 13:36
  • I think you should make it clear in the second part of your question that you want a DS with efficient min _and_ efficient find (and think about other operations). – paul-g Oct 23 '14 at 14:48
0

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();
Alexander Pogrebnyak
  • 44,836
  • 10
  • 105
  • 121
0

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.

StackFlowed
  • 6,664
  • 1
  • 29
  • 45
0

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 :)

Aye_baybae
  • 74
  • 3
  • 2
    `Collections#min` on the `entrySet` is certainly the right approach here. But you don't have to implement your own comparator: You can just use https://docs.oracle.com/javase/8/docs/api/java/util/Map.Entry.html#comparingByValue-- (and even if your implement your own: There is no need for overriding `equals` on that one...) – Marco13 Sep 23 '19 at 23:30
  • you are right in this case as its just an integer. I was trying to answer the question for a generic object case. It would be helpful for other people. – Aye_baybae Sep 24 '19 at 00:28