Leetcode 380. 常数时间插入、删除和获取随机元素
1.题目描述
设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构。
insert(val)
:当元素 val 不存在时,向集合中插入该项。remove(val)
:元素 val 存在时,从集合中移除该项。getRandom
:随机返回现有集合中的一项。每个元素应该有相同的概率被返回
示例:
// 初始化一个空的集合。
RandomizedSet randomSet = new RandomizedSet(); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomSet.insert(); // 返回 false ,表示集合中不存在 2 。
randomSet.remove(); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomSet.insert(); // getRandom 应随机返回 1 或 2 。
randomSet.getRandom(); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomSet.remove(); // 2 已在集合中,所以返回 false 。
randomSet.insert(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
randomSet.getRandom();
2.解题思路
分析:题目的难点在于有delete操作的情况下,要保证getRandom( )等概率随机返回集合中的一个元素。
一般地,题目的对时间复杂度的要求越高,都需要使用更多的辅助结构,以“空间换时间”。这里可以采用“两个哈希表”(多一个哈希表)或者“一个哈希表加一个数组”(多一个数组)。
渐进思路:
(1)没有delete(val),只有insert(val)和getRandom( )操作的情况下,连续的插入元素键值对<key,index>,因为index在逻辑上是连续,因此getRandom()等概率随机返回集合中的一个元素很容易实现,rand() % index (0~index-1)即可;
(2)有delete(val)操作,可以删除元素键值对之后,使得index不连续,中间有空洞,所以此时getRandom()产生的index可能正好是被删除的,导致时间复杂度超过O(1),所以delete(val)操作需要有一些限定条件,即保证每删除一个元素键值对之后,index个数减一,但是整体index在逻辑上是连续的。
例如:0~5 ——> 0~4 ——> 0~3
代码里关键部分有注释。
class RandomizedSet {
public:
/** Initialize your data structure here. */ //建立两个hash表,一个是<key,index>,另一个是<index,key>;
RandomizedSet() { } /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
bool insert(int val) {
//元素不存在时,插入
if(keyIndexMap.count(val) == )
{
keyIndexMap[val] = size;
indexKeyMap[size] = val;
++size;
return true;
}
return false;
} /** Removes a value from the set. Returns true if the set contained the specified element. */
bool remove(int val) {
////每删除一个键值对,用最后的键值对填充该空位,保证整个index在逻辑上连续,size减一
if(keyIndexMap.count(val) == )
{
int removeIndex = keyIndexMap[val];
int lastIndex = --size;//若size=1000,表示0~999;这里是取最后一个index int lastKey = indexKeyMap[lastIndex];
keyIndexMap[lastKey] = removeIndex;
indexKeyMap[removeIndex] = lastKey; keyIndexMap.erase(val);
indexKeyMap.erase(lastIndex);//下标方式取val对应的值index return true;
}
return false;
} /** Get a random element from the set. */
int getRandom() {
if (size == ) {
return NULL;
}
//srand((unsigned)time(NULL)); //去掉srand(),保证稳定的产生随机序列
int randomIndex = (int) (rand() % size); // 0 ~ size -1
return indexKeyMap[randomIndex];
} private:
map<int,int> keyIndexMap;
map<int,int> indexKeyMap;
int 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();
*/
3.用时更少的范例
这是Leetcode官网上C++完成此题提高的用时排名靠前的代码,这里与上面的解法差异就在于额外的辅助结构的选择,这里选的是在哈希表的基础上多增加一个数组,数组操作的时间复杂度和哈希表操作的时间复杂度均为O(1),但是数组时间复杂度O(1)的常数项更小,因此,这种解法效率更高。
class RandomizedSet {
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 (m.count(val)) return false;
nums.push_back(val);
m[val] = nums.size() - ;
return true;
} /** Removes a value from the set. Returns true if the set contained the specified element. */
bool remove(int val) {
if (!m.count(val)) return false;
int last = nums.back();
m[last] = m[val];
nums[m[val]] = last;
nums.pop_back();
m.erase(val);
return true;
} /** Get a random element from the set. */
int getRandom() {
return nums[rand() % nums.size()];
}
//private:
//注释掉private,提高一点速度
vector<int> nums;
unordered_map<int, int> m;
}; /**
* 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();
*/
Leetcode 380. 常数时间插入、删除和获取随机元素的更多相关文章
- Java实现 LeetCode 380 常数时间插入、删除和获取随机元素
380. 常数时间插入.删除和获取随机元素 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构. insert(val):当元素 val 不存在时,向集合中插入该项. remove( ...
- LeetCode 381. Insert Delete GetRandom O(1) - Duplicates allowed O(1) 时间插入、删除和获取随机元素 - 允许重复(C++/Java)
题目: Design a data structure that supports all following operations in averageO(1) time. Note: Duplic ...
- LeetCode380 常数时间插入、删除和获取随机元素
LeetCode380 常数时间插入.删除和获取随机元素 题目要求 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构. insert(val):当元素 val 不存在时,向集合中插 ...
- Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素 - 允许重复
381. O(1) 时间插入.删除和获取随机元素 - 允许重复 设计一个支持在平均 时间复杂度 O(1) 下, 执行以下操作的数据结构. 注意: 允许出现重复元素. insert(val):向集合中插 ...
- 381. O(1) 时间插入、删除和获取随机元素 - 允许重复
381. O(1) 时间插入.删除和获取随机元素 - 允许重复 LeetCode_381 题目详情 题解分析 代码实现 package com.walegarrett.interview; impor ...
- Leetcode 381. O(1) 时间插入、删除和获取随机元素 - 允许重复
1.题目描述 设计一个支持在平均 时间复杂度 O(1) 下, 执行以下操作的数据结构. 注意: 允许出现重复元素. insert(val):向集合中插入元素 val. remove(val):当 va ...
- LeetCode 380. Insert Delete GetRandom O(1) 常数时间插入、删除和获取随机元素(C++/Java)
题目: Design a data structure that supports all following operations in averageO(1) time. insert(val): ...
- LeetCode 哈希表 380. 常数时间插入、删除和获取随机元素(设计数据结构 List HashMap底层 时间复杂度)
比起之前那些问计数哈希表的题目,这道题好像更接近哈希表的底层机制. java中hashmap的实现是通过List<Node>,即链表的list,如果链表过长则换为红黑树,如果容量不足(装填 ...
- 381 Insert Delete GetRandom O(1) - Duplicates allowed O(1) 时间插入、删除和获取随机元素 - 允许重复
设计一个支持在平均 时间复杂度 O(1) 下, 执行以下操作的数据结构.注意: 允许出现重复元素. insert(val):向集合中插入元素 val. remove(val):当 val ...
随机推荐
- python内建模块Collections
# -*- coding:utf-8 -*- # OrderedDict可以实现一个FIFO(先进先出)的dict, # 当容量超出限制时,先删除最早添加的Key: from collections ...
- 【Python 开发】第二篇 :Python安装
一.python3.x安装 1)由于centos7原本就安装了Python2,而且这个Python2不能被删除,因为有很多系统命令,比如yum都要用到. 官网:https://www.python.o ...
- 基于Kubernetes(k8s)的RabbitMQ 集群
目前,有很多种基于Kubernetes搭建RabbitMQ集群的解决方案.今天笔者今天将要讨论我们在Fuel CCP项目当中所采用的方式.这种方式加以转变也适用于搭建RabbitMQ集群的一般方法.所 ...
- 【转】使用CNPM搭建私有NPM
最近的Node项目中因为数据模型等问题,需要有一个对各个模块进行统一的管理,如果把私有的模型publish到公共的npm不太合适,所以决定使用cnpm搭建一个私有的npm,同时也可以对项目常用的npm ...
- 使用Promise链式调用解决多个异步回调的问题
使用Promise链式调用解决多个异步回调的问题 比如我们平常经常遇到的一种情况: 网站中需要先获取用户名,然后再根据用户名去获取用户信息.这里获取用户名getUserName()和获取用户信息get ...
- 图的遍历——DFS(邻接矩阵)
递归 + 标记 一个连通图只要DFS一次,即可打印所有的点. #include <iostream> #include <cstdio> #include <cstdli ...
- android入门 — ListView
ListView主要是用来解决大量数据展示的问题,它的用途很广泛,几乎所有的app都会用到,比如说知乎.今日头条.微博.通讯录等. ListView允许用户通过上下滑动的方式将屏幕外的数据滚动到屏幕中 ...
- 项目UML设计(团队)
[团队信息] 团队项目: 小葵日记--主打记录与分享模式的日记app 队名:日不落战队 队员信息及贡献分比例: 短学号 名 本次作业博客链接 此次作业任务 贡献分配 备注 501 安琪 http:// ...
- iOS- 网络访问两种常用方式【GET & POST】实现的几个主要步骤
1.前言 上次,在博客里谈谈了[GET & POST]的区别,这次准备主要是分享一下自己对[GET & POST]的理解和实现的主要步骤. 在这就不多废话了,直接进主题,有什么不足的欢 ...
- [计算机网络] C++模拟telnet登陆SMTP服务发送邮件过程
在百度文库中的<使用telnet协议收发邮件>,我们可以很清楚地看到如何通过telnet来进行发送邮件,下面是一些需要用到的命令,通过以下命令可以很容易实现邮件发送功能.为了更好地理解其中 ...