【C++】每日一题 380 O(1)时间插入,删除和获取随机元素

2024-04-07 11:44

本文主要是介绍【C++】每日一题 380 O(1)时间插入,删除和获取随机元素,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

实现RandomizedSet 类:

RandomizedSet() 初始化 RandomizedSet 对象
bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。
bool remove(int val) 当元素 val 存在时,从集合中移除该项,并返回 true ;否则,返回 false 。
int getRandom() 随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。
你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1) 。

#include <unordered_map>
#include <vector>
#include <cstdlib>class RandomizedSet {
private:std::unordered_map<int, int> valToIndex; // 用于存储值到索引的映射std::vector<int> values; // 用于存储集合中的值public:/** Initialize your data structure here. */RandomizedSet() {}/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */bool insert(int val) {if (valToIndex.find(val) != valToIndex.end()) {return false; // 如果值已经存在,则返回false}values.push_back(val); // 在向量末尾插入新值valToIndex[val] = values.size() - 1; // 更新值到索引的映射return true;}/** Removes a value from the set. Returns true if the set contained the specified element. */bool remove(int val) {if (valToIndex.find(val) == valToIndex.end()) {return false; // 如果值不存在,则返回false}int index = valToIndex[val]; // 获取值在向量中的索引int lastVal = values.back(); // 获取向量中最后一个值values[index] = lastVal; // 将最后一个值移到被删除的位置valToIndex[lastVal] = index; // 更新最后一个值的索引values.pop_back(); // 删除最后一个值valToIndex.erase(val); // 删除值到索引的映射return true;}/** Get a random element from the set. */int getRandom() {int randomIndex = rand() % values.size(); // 生成随机索引return values[randomIndex]; // 返回随机索引处的值}
};

使用了一个 unordered_map 来存储值到索引的映射,以实现 O(1) 时间复杂度的插入和删除操作。同时,使用一个 vector 来存储集合中的值,并且通过将被删除元素与最后一个元素交换,然后再删除最后一个元素的方式来实现 O(1) 时间复杂度的删除操作。最后,getRandom 函数通过生成一个随机索引来随机返回集合中的一个元素。

易错点:
从 nums 向量中删除特定值 val 对应的元素,并且更新 indices 映射,使得其仍然与 nums 同步。以下代码错误示范

nums.erase(nums.begin()+indices[val]);
indices.erase(val);

假设 indices[val] 给出了 val 在 nums 中的索引位置。第一行代码是从 nums 向量中删除这个索引位置的元素,但是删除后,后面的元素会向前移动填补被删除的位置,这就会导致原来在 indices[val] 位置的元素变成了新的 val,而且 val 在 nums 中的位置已经改变了,所以删除 val 对应的索引值并不正确。

正确的做法是通过交换最后一个元素和要删除的元素来实现删除,然后更新映射。这就是下面的代码片段:

int index = indices[val];
int last = nums.back();
nums[index] = last;
indices[last] = index;
nums.pop_back();
indices.erase(val);

这样做的好处是,它避免了在删除元素后重新排序 nums 向量,同时保持了 indices 映射的正确性。

这篇关于【C++】每日一题 380 O(1)时间插入,删除和获取随机元素的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/882508

相关文章

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

Python的Darts库实现时间序列预测

《Python的Darts库实现时间序列预测》Darts一个集统计、机器学习与深度学习模型于一体的Python时间序列预测库,本文主要介绍了Python的Darts库实现时间序列预测,感兴趣的可以了解... 目录目录一、什么是 Darts?二、安装与基本配置安装 Darts导入基础模块三、时间序列数据结构与

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2

SpringBoot 获取请求参数的常用注解及用法

《SpringBoot获取请求参数的常用注解及用法》SpringBoot通过@RequestParam、@PathVariable等注解支持从HTTP请求中获取参数,涵盖查询、路径、请求体、头、C... 目录SpringBoot 提供了多种注解来方便地从 HTTP 请求中获取参数以下是主要的注解及其用法:1

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象