
这一题和https://blog.csdn.net/chaochen1407/article/details/102067676 只有两行之差。Duplicates allowed和linear related。这意思就是我可以多次插入一个元素并且根据我插入的次数它被random picked的概率也会相对应提高。譬如我insert(1) insert(2) insert(2),在这种情况下,我getRandom的时候,获得2的概率就是66%而获得1的概率就是33%。 在leetcode的题集里,往往改掉一个条件就能改变一个题目的性质,然而这题不是。这题的中心思想依旧和之前那题是差不多的。就是利用HashMap记录数字出现的位置和ArrayList装数据并且作getRandom处理,每当需要插入的时候,就往数组末尾插入,并且更新HashMap相关的位置信息,每当需要删除的时候,就和末尾的元素进行交换并且删除。只是因为duplicates allowed了,所以我们不能再用HashMap<Integer, Integer>这样的结构来记录一个数字的位置,相对应的,我们需要把它变成HashMap<Integer, Set<Integer>>,用set来装一堆位置而不仅仅是一个。每次,我们删除的时候,我们需要从Set里面选取一个元素进行交换或者删除,而不只是淡淡从map中删除掉数值对应的位置,只有当数值对应的Set为空的时候,我们就会知道当前数据里已经没有了那个数值,可以将Set本身也进行删除。先贴出代码,要注意代码中我们用到了一个新的数据结构LinkedHashSet,一会儿解释这是啥和为什么用这个。
class RandomizedCollection {
ArrayList<Integer> dataArr;
HashMap<Integer, Set<Integer>> indexMap;
Random random;
/** Initialize your data structure here. */
public RandomizedCollection() {
dataArr = new ArrayList<>();
indexMap = new HashMap<>();
random = new Random();
}
/** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */
public boolean insert(int val) {
dataArr.add(val);
Set<Integer> indexSet = null;
boolean result = true;
if (!indexMap.containsKey(val)) {
indexSet = new LinkedHashSet<>();
indexMap.put(val, indexSet);
} else {
result = false;
indexSet = indexMap.get(val);
}
indexSet.add(dataArr.size() - 1);
return result;
}
/** Removes a value from the collection. Returns true if the collection contained the specified element. */
public boolean remove(int val) {
Set<Integer> indexSet = indexMap.get(val);
if (indexSet == null) {
return false;
}
int lastVal = dataArr.get(dataArr.size() - 1);
Set<Integer> lastValSet = indexMap.get(lastVal);
if (lastVal != val) {
int valPos = indexSet.iterator().next();
dataArr.set(valPos, lastVal);
indexSet.remove(valPos);
lastValSet.add(valPos);
if (indexSet.isEmpty()) {
indexMap.remove(val);
}
}
lastValSet.remove(dataArr.size() - 1);
if (lastValSet.isEmpty()) {
indexMap.remove(lastVal);
}
dataArr.remove(dataArr.size() - 1);
return true;
}
/** Get a random element from the collection. */
public int getRandom() {
int next = random.nextInt(dataArr.size());
return dataArr.get(next);
}
}
LinkedHashSet: https://docs.oracle.com/javase/9/docs/api/java/util/LinkedHashSet.html
这基本上就是一个HashSet和Doubly linked list的合体,所以在每次操作上,都多了一些overhead所以稍微会比正常的HashSet更慢。但它有俩个好处:1.如果你用iterator遍历它,它输出的顺序会和你插入的顺序是相同的。2. iterator().next()保证了能够O(1)的速率访问到这个Set里面的头节点,这个是HashSet办不到的。其实这里我们也不是真的需要访问头节点,我们只需要访问Set里面任何一个节点即可。可以看得出来,getRandom()和之前那题一摸一样,insert()和remove的差别也就在于我们之前是在HashMap里对一个特定的数值进行操作,而现在我们操作的对象是一个Set。整个思路基本是一致的。
本文深入探讨了一种允许重复元素的随机集合数据结构实现,通过使用ArrayList和HashMap配合LinkedHashSet,确保了元素插入、删除和随机获取操作的高效执行。特别关注了如何处理重复元素及其概率加权随机选择。
790

被折叠的 条评论
为什么被折叠?



