O(1) 时间插入、删除和获取随机元素 - 允许重复
难度:
标签:
题目描述
RandomizedCollection
是一种包含数字集合(可能是重复的)的数据结构。它应该支持插入和删除特定元素,以及删除随机元素。
实现 RandomizedCollection
类:
RandomizedCollection()
初始化空的RandomizedCollection
对象。bool insert(int val)
将一个val
项插入到集合中,即使该项已经存在。如果该项不存在,则返回true
,否则返回false
。bool remove(int val)
如果存在,从集合中移除一个val
项。如果该项存在,则返回true
,否则返回false
。注意,如果val
在集合中出现多次,我们只删除其中一个。int getRandom()
从当前的多个元素集合中返回一个随机元素。每个元素被返回的概率与集合中包含的相同值的数量 线性相关 。
您必须实现类的函数,使每个函数的 平均 时间复杂度为 O(1)
。
注意:生成测试用例时,只有在 RandomizedCollection
中 至少有一项 时,才会调用 getRandom
。
示例 1:
输入 ["RandomizedCollection", "insert", "insert", "insert", "getRandom", "remove", "getRandom"] [[], [1], [1], [2], [], [1], []] 输出 [null, true, false, true, 2, true, 1] 解释 RandomizedCollection collection = new RandomizedCollection();// 初始化一个空的集合。 collection.insert(1); // 返回 true,因为集合不包含 1。 // 将 1 插入到集合中。 collection.insert(1); // 返回 false,因为集合包含 1。 // 将另一个 1 插入到集合中。集合现在包含 [1,1]。 collection.insert(2); // 返回 true,因为集合不包含 2。 // 将 2 插入到集合中。集合现在包含 [1,1,2]。 collection.getRandom(); // getRandom 应当: // 有 2/3 的概率返回 1, // 1/3 的概率返回 2。 collection.remove(1); // 返回 true,因为集合包含 1。 // 从集合中移除 1。集合现在包含 [1,2]。 collection.getRandom(); // getRandom 应该返回 1 或 2,两者的可能性相同。
提示:
-231 <= val <= 231 - 1
insert
,remove
和getRandom
最多 总共 被调用2 * 105
次- 当调用
getRandom
时,数据结构中 至少有一个 元素
代码结果
运行时间: 297 ms, 内存: 69.7 MB
// 思路: 使用Java Stream实现 insert, remove, getRandom 方法。
// 利用Collectors.groupingBy收集元素及其索引,借助 Optional 操作来处理移除元素。
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class RandomizedCollectionStream {
private List<Integer> nums;
private Map<Integer, Set<Integer>> locs;
private Random rand;
public RandomizedCollectionStream() {
nums = new ArrayList<>();
locs = new HashMap<>();
rand = new Random();
}
public boolean insert(int val) {
boolean notContains = locs.computeIfAbsent(val, k -> new HashSet<>()).isEmpty();
locs.get(val).add(nums.size());
nums.add(val);
return notContains;
}
public boolean remove(int val) {
return Optional.ofNullable(locs.get(val))
.filter(set -> !set.isEmpty())
.map(set -> {
int loc = set.iterator().next();
set.remove(loc);
if (set.isEmpty()) locs.remove(val);
int lastElement = nums.get(nums.size() - 1);
nums.set(loc, lastElement);
locs.get(lastElement).add(loc);
locs.get(lastElement).remove(nums.size() - 1);
nums.remove(nums.size() - 1);
return true;
})
.orElse(false);
}
public int getRandom() {
return nums.stream()
.skip(rand.nextInt(nums.size()))
.findFirst()
.orElseThrow();
}
}
解释
方法:
该题解使用哈希表 pos 和动态数组 value 来实现 O(1) 时间复杂度的插入、删除和获取随机元素操作。其中,哈希表 pos 用于存储每个元素在 value 中出现的下标位置集合,动态数组 value 用于存储实际的元素值。在插入时,将元素追加到 value 的末尾,并在 pos 中记录该元素的下标;在删除时,将要删除的元素与 value 的最后一个元素交换位置,然后删除最后一个元素,并更新 pos 中的下标信息;在获取随机元素时,直接从 value 中随机选择一个元素返回即可。
时间复杂度:
O(1)
空间复杂度:
O(n)
代码细节讲解
🦆
在`RandomizedCollection`中,如果`val`值重复多次,如何确保`remove`方法只删除一个实例?
▷🦆
在完成删除操作时,你提到需要交换要删除的元素与数组最后一个元素的位置。这种方法在所有情况下都有效吗?例如,如果要删除的元素已经是数组中的最后一个元素,这里的逻辑是否需要调整?
▷🦆
当使用动态数组`value`和哈希表`pos`时,删除或插入操作是否可能导致哈希表和数组之间的不同步?例如,在删除操作中,如果`pop`操作或`set`更新失败了怎么办?
▷🦆
为什么在获取随机元素时,每个元素被返回的概率与集合中包含的相同值的数量线性相关?这是否意味着数组中每个位置被选中的可能性相等?
▷相关问题
O(1) 时间插入、删除和获取随机元素
实现RandomizedSet
类:
RandomizedSet()
初始化RandomizedSet
对象bool insert(int val)
当元素val
不存在时,向集合中插入该项,并返回true
;否则,返回false
。bool remove(int val)
当元素val
存在时,从集合中移除该项,并返回true
;否则,返回false
。int getRandom()
随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。
你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1)
。
示例:
输入 ["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"] [[], [1], [2], [2], [], [1], [2], []] 输出 [null, true, false, true, 2, true, false, 2] 解释 RandomizedSet randomizedSet = new RandomizedSet(); randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。 randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。 randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。 randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。 randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。 randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。 randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
提示:
-231 <= val <= 231 - 1
- 最多调用
insert
、remove
和getRandom
函数2 *
105
次 - 在调用
getRandom
方法时,数据结构中 至少存在一个 元素。