【哈希映射】【 哈希集合】 381. O(1) 时间插入、删除和获取随机元素 - 允许重复

简介: 【哈希映射】【 哈希集合】 381. O(1) 时间插入、删除和获取随机元素 - 允许重复

本文涉及知识点

哈希映射 哈希集合

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++**实现。

相关文章
|
安全 网络协议 物联网
AliOS Things开发前准备 |《AliOS Things快速开发指南》
在运行AliOS Things系统之前,您需要做好一系列准备工作,包括搭建环境、安装驱动设备、下载AliOS Things系统源码、安装开发工具AliOS Studio等。本文详细介绍如何完成这些准备工作。
AliOS Things开发前准备 |《AliOS Things快速开发指南》
|
8月前
|
人工智能 自然语言处理 程序员
下载量突破400万,百万开发者首选的 AI 编码工具通义灵码是如何炼成的?
下载量突破400万,百万开发者首选的 AI 编码工具通义灵码是如何炼成的?
310 3
|
JavaScript 前端开发 开发者
React 的正确使用方法:ref 篇
你真的用对了 useRef 吗?在与 TypeScript 一起使用、以及撰写组件库的情况下,你的写法能够避开以下所有场景的坑吗?
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
336 7
|
前端开发 JavaScript Java
JPOM尝试
JPOM 尝试
394 2
|
算法 调度
【完全复现】基于改进粒子群算法的微电网多目标优化调度
该文档描述了一个使用改进粒子群算法实现的微电网多目标优化调度的Matlab程序。该模型旨在最小化运行成本和环境保护成本,将多目标问题通过权值转换为单目标问题解决。程序中定义了决策变量,如柴油发电机、微型燃气轮机、联络线和储能的输出,并使用全局变量处理电负荷、风力和光伏功率等数据。算法参数包括最大迭代次数和种群大小。代码调用了`PSOFUN`函数来执行优化计算,并展示了优化结果的图表。
|
存储 关系型数据库 MySQL
【赵渝强老师】MySQL的Memory存储引擎
MySQL 的存储引擎层负责数据的存储和提取,支持多种存储引擎,如 InnoDB、MyISAM 和 Memory。InnoDB 是最常用的存储引擎,从 MySQL 5.5.5 版本起成为默认引擎。Memory 存储引擎的数据仅存在于内存中,重启后数据会丢失。示例中创建了使用 Memory 引擎的 test3 表,并展示了数据在重启后消失的过程。
226 0
|
机器学习/深度学习 缓存 数据可视化
Streamlit入门指南
Streamlit是Python库,用于创建交互式数据科学和机器学习Web应用。它简化了定制Web应用的创建,提供内置小部件和工具进行数据展示、用户输入处理和自定义可视化。快速入门涉及安装Streamlit、导入库、定义应用并使用`streamlit run`命令运行。示例代码展示了如何创建一个显示滑块和正弦图的应用。最佳实践包括组织代码、利用缓存、优化布局以及使用内置功能。Streamlit Gallery提供了更多应用示例,如文本生成器和图像分类器。
1423 0
|
存储 JSON 分布式计算
一体化大数据智能计算平台 ODPS 产品年度发布
阿里云ODPS全新升级,存储、调度、元数据一体化融合 ,从 Processing 升级为 Platform,即 Open Data Platform and Service。本次峰会,同步发布了新的产品能力,即MaxCompute 引擎新功能发布及Hologres 引擎新功能发布。
一体化大数据智能计算平台 ODPS 产品年度发布
|
机器学习/深度学习 数据采集 数据可视化
【机器学习sklearn】简单易懂核密度估计KernelDensity
【机器学习sklearn】简单易懂核密度估计KernelDensity
1886 0
【机器学习sklearn】简单易懂核密度估计KernelDensity