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();
設計一個數據結構,支持可以在O(1)的時間內完成對數字的插入,刪除和獲取隨機數的操做,容許插入重複的數字,同時要求每一個數字被隨機獲取的機率和該數字當前在數據結構中的個數成正比。java
強烈建議先看一下這個問題的基礎版本,傳送門在這裏。segmentfault
遵循以前基礎版本的思路,當解決這種問題的時候咱們會用數組和hashmap來作位置的存儲,從而更新的時候無需檢索。可是在這題的情境下,存在一個問題,舉個例子:
假如如今插入1,2,3,3,4,3,3
此時的map中應當是以下:1:[0] 2:[1] 3:[2,3,5,6] 4:[4]
咱們先執行刪除1,按照以前的規則,咱們會刪除數組中最後一個元素,並將其值移動到這個位置上map應當被更新爲2:[1] 3:[2,3,5,0] 4:[4]
接着咱們再刪除2,此時雖然最後一個元素仍是3,可是這個3在位置數組中的位置倒是須要O(n)的時間來查詢的,這就違反了O(1)的刪除時間複雜度。數組
網上有一些java實現採用OrderSet來解決,這是不合理的。由於有序堆本質上底層是一個最大堆或最小堆,它的插入和刪除操做都須要O(lgn)的時間複雜度來完成數據結構
這裏咱們採用的方式是繼續冗餘,即咱們在插入每個元素的時候,同時記錄該元素在下標數組中的位置,舉個例子:
先插入1,則map的值爲[1:[0]]
,list的值爲[[1,0]]
此處的0表明1這個值在下標數組[0]中位於第0個位置上。
在插入2,則map的值爲[1:[0], 2:[1]]
, list的值爲[[1,0],[2,0]]
再插入1,此時map=[1:[0, 2], 2:[1]
, list的值爲[[1,0],[2,0],[1,1]]
此時刪除2,同理,咱們仍是會將數組中最後一個元素的值替換在刪除掉元素的位置,此處咱們從map中得出2最後一次在數組中出現的下標爲1,咱們須要將最後位置上的1替換掉當前2的值,以後咱們還能從數組中得知,1這個數字它對應的位置下標的索引爲2,所以咱們再將map[1]
中map[1][2]
的值替換爲2所在的新的位置,即1。此時的map=[1:[0, 1], 2:[]
list=[[1,0], [1,1]]
dom
代碼以下:this
public class InsertDeleteGetRandomDuplicatesallowed_381 { private List<Pair> list; private Map<Integer, List<Integer>> index; /** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */ public InsertDeleteGetRandomDuplicatesallowed_381() { list = new ArrayList<>(); index = new HashMap<>(); } public boolean insert(int val) { boolean contains = true; if(!index.containsKey(val) || index.get(val).isEmpty()) { contains = false; } List<Integer> tmp = index.getOrDefault(val, new ArrayList<>()); tmp.add(list.size()); index.put(val, tmp); list.add(new Pair(val, tmp.size()-1)); return !contains; } /** Removes a value from the collection. Returns true if the collection contained the specified element. */ public boolean remove(int val) { if(!index.containsKey(val) || index.get(val).isEmpty()) { return false; } List<Integer> tmp = index.get(val); int position = tmp.remove(tmp.size()-1); if(position != list.size()-1) { Pair lastPair = list.get(list.size()-1); int lastValue = lastPair.value; List<Integer> lastValuePositions = index.get(lastValue); lastValuePositions.set(lastPair.position, position); list.set(position, lastPair); } list.remove(list.size()-1); return true; } /** Get a random element from the collection. */ public int getRandom() { int position = (int)Math.floor((Math.random() * list.size())); return list.get(position).value; } public static class Pair{ int value; int position; public Pair(int value, int position) { this.value = value; this.position = position; } } }