MultiSet is a generalization of the concept of a set. Its main idea is to allow a member of a multiset to have zero, one, or more than one occurrence in the multiset. In MultiSet, we can delete one occurrence of a member as well as all the occurrences of a member of MultiSet. Java has Set interface which allows you to add and remove elements in O(log(n)) time where n is the number of distinct elements present in the set. Here Set does not allow multiple elements to be present, and for that reason, different languages provide an inbuilt option of multiset which allows the insertion and deletion of elements in O(log(n)) time and O(n) space. Java has provided Guava’s Multiset, in which the duplicate element will not be discarded.
Illustration:
Java
import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; // Multiset of String Multiset<Integer> multiset = HashMultiset.create(); // Adding elements to the set multiset.add( 1 ); multiset.add( 1 ); System.out.println(multiset); |
Below we show how TreeMap in java can be used to implement Multiset. We can create MultiSet in java using TreeMap which provides guaranteed log((n)) time cost for the get, put, remove, and contains key operations. We will use treemap, in which the key will be the element added in the multiset, and the value will represent the number of occurrences of that element in the multiset. There are two essential operations that need to be implemented in MultiSet: add and remove
add operation adds an element to the multiset while the remove operation delete the element from the multiset. Remove operation can also have two variations, one is removing all occurrences of the element while the other is removing only one occurrence of the element. Both variations of the delete operation have been taken care of in the implementation
Add Operation Implementation
i) First, we need to create a TreeMap, let’s call it Map
TreeMap<Integer, Integer> map=new TreeMap<>(); // here key will be the element in the multiset, // and value will be the number of occurrence // of the element in the multiset
ii) For adding an element we need to consider two cases:
Case 1: If the element is already present in the multiset, in that case, the element is already present in the map as well. So we just increment the value of the corresponding element by 1.
map.put(key, map.get(key) + 1); // increment key value by 1 indicating an extra // occurrence of key has been added in the multiset
Case 2: If the element is not present in the multiset, in the case element will also be not present in the map as well. So we have to add the element in the map using the put method.
map.put(key, 1); // put method add key in the map, value 1 indicates // that only one occurrence of key is present // in the multiset.
The time complexity of the put() and get() method in TreeMap is O(log(n)) where n is the number of key values, hence the time complexity to perform add operation is O(log(n)).
Delete Operation Implementation
Deleting only one occurrence of the element:
i) For removing only one occurrence of the element we again use the put and get method
map.put(key, map.get(key)-1); // decrease the occurrence of key by 1 indication // the removal of key from multiset by 1
ii) For removing all occurrences of the element we can use the remove method of the treemap.
map.remove(key); // remove the key from multiset
The time complexity of the remove() method in TreeMap is O(log(n)), hence the complexity to perform the deletion operation is also O(log(n)). We can also get the total number of occurrences of a particular element just by using the get(element) method of TreeMap,
int count(int a) { // count occurrence of a return map.getOrDefault(a, 0); }
For knowing the size of the multiset we can create a global variable size, when adding size we decrement size, and while deleting we decrement size.
Ceiling Operation
we can find the smallest element greater than or equal to a particular element key by using the ceiling() method of treemap which has the time complexity of O(log(n)).
int ceiling(int a) { // finds smallest element // greater than or equal to a if (map.ceilingKey(a) != null) { int find = map.ceilingKey(a); return find; } else return Integer.MIN_VALUE; }
floor Operation:
we can find the largest element smaller than or equal to a particular element key by using the floor() method of treemap which has the time complexity of O(log(n)).
int floor(int a) { // finds largest element less // than or equal to a if (map.floorKey(a) != null) { int find = map.floorKey(a); return find; } else return Integer.MAX_VALUE; }
lower operation:
we can find the largest element smaller than a particular element key by using the lower() method of treemap which has the time complexity of O(log(n)).
int lower(int a) { // finds largest element smaller than a if (map.lowerKey(a) != null) { int find = map.lowerKey(a); return find; } else return Integer.MAX_VALUE; }
higher operation:
finds the smallest element larger than a particular element key by using the higher() method of treemap which has the time complexity of O(log(n)).
int higher(int a) { // finds smallest element greater than a if (map.higherKey(a) != null) { int find = map.higherKey(a); return find; }else return Integer.MIN_VALUE; }
The total space complexity to implement a multiset using treemap is O(n) for storing the key-value pairs. The complete implementation of the above operation described has been provided below:
Java
/*package whatever // do not write package name here */ import java.io.*; import java.lang.*; import java.util.*; class GFG { public static class MultiSet { public TreeMap<Integer, Integer> map; public int size = 0 ; public MultiSet() { // constructor for empty multiset map = new TreeMap<>(); size = 0 ; } public MultiSet( int [] a) { // constructor to create a multiset with array map = new TreeMap<>(); size = a.length; for ( int i = 0 ; i < a.length; i++) { map.put(a[i], map.getOrDefault(a[i], 0 ) + 1 ); } } void add( int a) { // add element in multiset size++; map.put(a, map.getOrDefault(a, 0 ) + 1 ); } void remove( int a) { // remove element from multiset size -= map.getOrDefault(a, 0 ); map.remove(a); } int count( int a) { // count occurrence of a return map.getOrDefault(a, 0 ); } int ceiling( int a) { // finds smallest element greater than or equal to // a if (map.ceilingKey(a) != null ) { int find = map.ceilingKey(a); return find; } else return Integer.MIN_VALUE; } int floor( int a) { // finds largest element less than or equal to a if (map.floorKey(a) != null ) { int find = map.floorKey(a); return find; } else return Integer.MAX_VALUE; } int lower( int a) { // finds largest element smaller than a if (map.lowerKey(a) != null ) { int find = map.lowerKey(a); return find; } else return Integer.MAX_VALUE; } int higher( int a) { // finds smallest element greater than a if (map.higherKey(a) != null ) { int find = map.higherKey(a); return find; } else return Integer.MIN_VALUE; } int first() { // return smallest element in multiset return map.firstKey(); } int last() { // return largest element in multiset return map.lastKey(); } boolean contains( int a) { if (map.containsKey(a)) return true ; return false ; } int size() { return size; } void clear() { map.clear(); } } public static void main(String[] args) { MultiSet ms = new MultiSet(); ms.add( 1 ); ms.add( 2 ); ms.add( 3 ); ms.add( 2 ); ms.add( 3 ); ms.add( 3 ); ms.add( 3 ); System.out.println( "Occurrence of 1 is " + ms.count( 1 )); System.out.println( "Occurrence of 2 is " + ms.count( 2 )); System.out.println( "Occurrence of 3 is " + ms.count( 3 )); System.out.println( "First element greater than 2 is " + ms.higher( 2 )); } } |
Output:
Occurrence of 1 is 1 Occurrence of 2 is 2 Occurrence of 3 is 4 First element greater than 2 is 3
In the above code, you can also find the floor, ceiling, and higher in O(log(n)) time.