Design a data structure that supports all following operations in average O(1) time.
Note: Duplicate elements are allowed.
insert(val)
: Inserts an item val to the collection.remove(val)
: Removes an item val from the collection if present.getRandom
: Returns a random element from current collection of elements. The probability of each element being returned is linearly related to the number of same value the collection contains.
Example:
// Init an empty collection. RandomizedCollection collection = new RandomizedCollection(); // Inserts 1 to the collection. Returns true as the collection did not contain 1. collection.insert(1); // Inserts another 1 to the collection. Returns false as the collection contained 1. Collection now contains [1,1]. collection.insert(1); // Inserts 2 to the collection, returns true. Collection now contains [1,1,2]. collection.insert(2); // getRandom should return 1 with the probability 2/3, and returns 2 with the probability 1/3. collection.getRandom(); // Removes 1 from the collection, returns true. Collection now contains [1,2]. collection.remove(1); // getRandom should return 1 and 2 both equally likely. collection.getRandom();
Analysis:
Because duplicates are allowed, we use a HashSet to store every index of a number.
1 import java.util.Random; 2 3 public class RandomizedCollection { 4 HashMap<Integer, HashSet<Integer>> numMap; 5 List<Integer> numList; 6 7 /** Initialize your data structure here. */ 8 public RandomizedCollection() { 9 numMap = new HashMap<Integer, HashSet<Integer>>(); 10 numList = new ArrayList<Integer>(); 11 } 12 13 /** 14 * Inserts a value to the collection. Returns true if the collection did 15 * not already contain the specified element. 16 */ 17 public boolean insert(int val) { 18 boolean hasVal = false; 19 if (numMap.containsKey(val)) { 20 hasVal = true; 21 numMap.get(val).add(numList.size()); 22 numList.add(val); 23 } else { 24 HashSet<Integer> set = new HashSet<Integer>(); 25 set.add(numList.size()); 26 numMap.put(val, set); 27 numList.add(val); 28 } 29 return !hasVal; 30 } 31 32 /** 33 * Removes a value from the collection. Returns true if the collection 34 * contained the specified element. 35 */ 36 public boolean remove(int val) { 37 if (!numMap.containsKey(val)) 38 return false; 39 40 // Get an index of @val from index set and remove this index from 41 // the set. 42 HashSet<Integer> indSet = numMap.get(val); 43 int ind = indSet.iterator().next(); 44 indSet.remove(ind); 45 46 // Delete the entry, if the index list becomes empty. 47 if (indSet.isEmpty()) { 48 numMap.remove(val); 49 } 50 51 52 // Remove the indexed number from @numList by swapping it to the tail. 53 // Modify the index of the tail number. 54 // NOTE: it is important to check whether @ind is the tail of @numList! 55 if (ind!=numList.size()-1){ 56 int tailNum = numList.get(numList.size() - 1); 57 numMap.get(tailNum).remove(numList.size() - 1); 58 numMap.get(tailNum).add(ind); 59 numList.set(ind, tailNum); 60 } 61 numList.remove(numList.size() - 1); 62 63 return true; 64 } 65 66 /** Get a random element from the collection. */ 67 public int getRandom() { 68 Random engine = new Random(); 69 int ind = engine.nextInt(numList.size()); 70 return numList.get(ind); 71 } 72 }