题目要求
实现 RandomizedSet 类:
- RandomizedSet() 初始化 RandomizedSet 对象。
- bool insert(int val) 将不存在的项目 val 插入随机集合。如果项目不存在,则返回 true,否则返回 false。
- bool remove(int val) 从集合中删除项目 val(如果存在)。如果存在,则返回 true,否则返回 false。
- int getRandom() 从当前元素集中随机返回一个元素(保证调用此方法时至少有一个元素存在)。每个元素被返回的概率必须相同。
您必须实现该类的函数,使每个函数的平均工作时间复杂度为 O(1)。
思路
我不会做,不是很熟悉,参考labuladong,东哥的思路和题解。常数时间删除-查找数组中的任意元素 | labuladong 的算法笔记Info 数据结构精品课 (https://aep.h5.xeknow.com/s/1XJHEO) 和 递归算法专题课 (https://aep.xet.tech/s/3YGcq3) 限时附赠网站会员;算法可视化编辑器上线,点击体验 (https://labuladong.online/algo-visualize/)! 读完本文,你不仅学会了算法套路...https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-48c1d/chang-shu--6b296/#%E5%AE%9E%E7%8E%B0%E9%9A%8F%E6%9C%BA%E9%9B%86%E5%90%88
对于一个标准的 HashSet
,你能否在 O(1)
的时间内实现 getRandom
函数?
其实是不能的,因为根据刚才说到的底层实现,元素是被哈希函数「分散」到整个数组里面的,更别说还有拉链法等等解决哈希冲突的机制,基本做不到 O(1)
时间等概率随机获取元素。
换句话说,对于 getRandom
方法,如果想「等概率」且「在 O(1)
的时间」取出元素,一定要满足:
底层用数组实现,且数组必须是紧凑的,这样我们就可以直接生成随机数作为索引,选取随机元素。
但如果用数组存储元素的话,常规的插入,删除的时间复杂度又不可能是 O(1)
。
然而,对数组尾部进行插入和删除操作不会涉及数据搬移,时间复杂度是 O(1)
。
所以,如果我们想在 O(1)
的时间删除数组中的某一个元素 val
,可以先把这个元素交换到数组的尾部,然后再 pop
掉。
代码
class RandomizedSet {
public:
vector<int> nums;
// 记录每个元素对应在 nums 中的索引
unordered_map<int, int> valToIndex;
RandomizedSet() {
}
bool insert(int val) {
if (valToIndex.count(val)) {
return false;
}
// 若 val 不存在,插入到 nums 尾部,
// 并记录 val 对应的索引值
valToIndex[val] = nums.size();
nums.push_back(val);
return true;
}
bool remove(int val) {
if (!valToIndex.count(val)) {
return false;
}
int index = valToIndex[val];
// 将最后一个元素对应的索引修改为 index
valToIndex[nums.back()] = index;
// 交换 val 和最后一个元素
swap(nums[index], nums.back());
// 在数组中删除元素 val
nums.pop_back();
// 删除元素 val 对应的索引
valToIndex.erase(val);
return true;
}
int getRandom() {
// 随机获取 nums 中的一个元素
return nums[rand() % nums.size()];
}
};
/**
* Your RandomizedSet object will be instantiated and called as such:
* RandomizedSet* obj = new RandomizedSet();
* bool param_1 = obj->insert(val);
* bool param_2 = obj->remove(val);
* int param_3 = obj->getRandom();
*/