实现 RandomizedSet 类:
您必须实现该类的函数,使每个函数的平均工作时间复杂度为 O(1)。
对于一个标准的?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();
*/