1. 题目来源
链接:lc381. O(1) 时间插入、删除和获取随机元素 - 允许重复
2. 题目说明
3. 题目解析
和 [M设计+哈希表] lc380. 常数时间插入、删除和获取随机元素(设计+哈希表) 思路一致,现在要求允许重复元素。
设计数据结构,实现三个操作:
- O ( 1 ) O(1) O(1) 插入
- O ( 1 ) O(1) O(1) 删除
- O ( 1 ) O(1) O(1) 返回一个随机值,每个数返回的概率要和它出现的次数成正比。
依旧哈希表与数组相结合。在此数组不需要进行改动,但是哈希表需要稍加改动,将其扩展为哈希表套哈希表。在以往哈希表 <val, index>
这样的一对一映射关系,我们可将其改动为 <val, hash>
这样的一对多的映射关系,维护 val
在数组中所有的不同下标就可以了。
至于插入删除还都是老样子了。可查看 [M设计+哈希表] lc380. 常数时间插入、删除和获取随机元素(设计+哈希表)
还有这个 rand()
函数,它在 Windows
下返回值是有界的,貌似最大是一个 32767 的数,所以当数组下标大于这个值时,后面的可能随机不到了。但在 Linux
下它的返回值是很大很大的。 参考 大佬:C语言中的rand()函数
那么在 Windows
下想要获取一个大的随机数,就需要自己组合,即做两次 rand()
操作,就是对其高 15 位和低 15 位均做一次 rand()
操作,就可以了。
代码:
class RandomizedCollection {
public:
unordered_map<int, unordered_set<int>> hash;
vector<int> nums;
/** Initialize your data structure here. */
RandomizedCollection() {
}
/** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */
bool insert(int x) {
bool res = hash[x].empty();
nums.push_back(x);
hash[x].insert(nums.size() - 1);
return res;
}
/** Removes a value from the collection. Returns true if the collection contained the specified element. */
bool remove(int x) {
if (hash[x].size()) {
int px = *hash[x].begin(), py = nums.size() - 1;
int y = nums.back();
swap(nums[px], nums[py]);
hash[x].erase(px), hash[x].insert(py);
hash[y].erase(py), hash[y].insert(px);
nums.pop_back();
hash[x].erase(py);
return true;
}
return false;
}
/** Get a random element from the collection. */
int getRandom() {
return nums[rand() % nums.size()];
}
};
/**
* Your RandomizedCollection object will be instantiated and called as such:
* RandomizedCollection* obj = new RandomizedCollection();
* bool param_1 = obj->insert(val);
* bool param_2 = obj->remove(val);
* int param_3 = obj->getRandom();
*/