作者推荐
视频算法专题
本文涉及知识点
哈希映射 哈希集合
LeetCode 381. 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 时,数据结构中 至少有一个 元素
哈希
用向量记录值,追加的时候时间复杂度是O1),删除是O(n)。可以将要删除的元素和尾元素互换,然后删除。
 unordered_map<int, unordered_set> m_valueIndex; 记录各值在m_nums中的下标。
 注意:
 要删除的元素就是末尾,要特殊处理。否则会删除下标失败,而多一个下标。
代码
核心代码
class RandomizedCollection {
public:RandomizedCollection() {srand(time(nullptr));std::cout << "==" << std::endl;}bool insert(int val) {bool b = m_valueIndex[val].empty();m_valueIndex[val].emplace(m_nums.size());m_nums.emplace_back(val);std::cout << m_nums.size() << std::endl;return b;}bool remove(int val) {if (m_valueIndex[val].empty()){return false;}const int delIndex = *m_valueIndex[val].begin();m_valueIndex[val].erase(m_valueIndex[val].begin());const int endValue = m_nums.back();if (m_valueIndex[endValue].count(m_nums.size() - 1)){m_valueIndex[endValue].erase(m_nums.size() - 1);m_valueIndex[endValue].emplace(delIndex);m_nums[delIndex] = endValue;}m_nums.pop_back();std::cout << "del " << m_nums.size() << std::endl;return true;}int getRandom() {std::cout << "getRandom " << m_nums.size() << std::endl;return m_nums[rand() % m_nums.size()];}unordered_map<int, unordered_set<int>> m_valueIndex;vector<int> m_nums;
};
2023年5月
class RandomizedCollection {
 public:
 RandomizedCollection() {
 srand(time(nullptr));
 }
 bool insert(int val) {
 m_mValueIndexs[val].emplace(m_iSize);
 m_arr[m_iSize] = val;
 m_iSize++; 
 return 1 == m_mValueIndexs[val].size();
 }
 bool remove(int val) { 
 if (m_mValueIndexs.count(val))
 {
 int index = *m_mValueIndexs[val].begin();
 m_mValueIndexs[val].erase(index);
 Erase(index); 
 if (m_mValueIndexs[val].empty())
 {
 m_mValueIndexs.erase(val);
 }
 return true;
 }
 return false;
 }
 int getRandom() {
 int index = rand() % m_iSize;
 return m_arr[index];
 }
 void Erase(int index)
 { 
 if (m_iSize - 1 == index)
 {
 m_iSize–;
 return;
 }
 const int iEndValue = m_arr[m_iSize - 1];
 m_mValueIndexs[iEndValue].erase(m_iSize - 1);
 m_mValueIndexs[iEndValue].insert(index);
 std::swap(m_arr[m_iSize - 1], m_arr[index]);
 m_iSize–;
 }
 std::unordered_map<int, std::unordered_set> m_mValueIndexs;
 int m_arr[1000 * 200];
 int m_iSize = 0;
 };

扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
 https://edu.csdn.net/course/detail/38771
如何你想快
速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
 https://edu.csdn.net/lecturer/6176
相关
下载
想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
 https://download.csdn.net/download/he_zhidan/88348653
| 我想对大家说的话 | 
|---|
| 闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 | 
| 子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 | 
| 如果程序是一条龙,那算法就是他的是睛 | 
测试环境
操作系统:win7 开发环境: VS2019 C++17
 或者 操作系统:win10 开发环境: VS2022 **C+
+17**
 如无特殊说明,本算法用**C++**实现。
