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 ...
随机推荐
- 数数字 (Digit Counting,ACM/ICPC Dannang 2007 ,UVa1225)
题目描述:算法竞赛入门经典习题3-3 #include <stdio.h> #include <string.h> int main(int argc, char *argv[ ...
- C++ ifndef /define/ endif 作用和用法
ifndef/define/endif”主要目的是防止头文件的重复包含和编译 比如你有两个C文件,这两个C文件都include了同一个头文件.而编译时,这两个C文件要一同编译成一个可运行文件,于是问题 ...
- 2.重新安装CM服务
步骤1.停止CM服务2.删除CM服务3.添加CM服务4.测试数据库 步骤 1.停止CM服务 2.删除CM服务 没有发现可以单独删除某一项CM服务,必须全部删除 3.添加CM服务 4.测试数据库 如果报 ...
- appcan打包后产生的问题总结
以appcan为基础的项目,最终需要打包后进行调试.在调试过程中,主要的样式问题在苹果手机上,下面将这些问题总结起来,以防下次再犯. 1:ios 7 以上的手机中,状态栏与内容重叠: 问题描述:在io ...
- this指针与const成员函数
this指针的类型为:classType *const // 即指向类类型非常量版本的常量指针 所以,我们不能把this绑定到一个常量对象上 ===> 不能在一个常量对象上调用普通的 ...
- 软件管理——rpm&dpkg、yum&apt-get
一般来说著名的linux系统基本上分两大类: 1. RedHat系列:Redhat.Centos.Fedora等 2. Debian系列:Debian.Ubuntu等 一.RedHat 系列 ...
- Coins and Queries(map迭代器+贪心)
题意 n个硬币,q次询问.第二行给你n个硬币的面值(保证都是2的次幂!).每次询问组成b块钱,最少需要多少个硬币? Example Input 5 42 4 8 2 4851410 Output 1- ...
- Alphabetic Removals(模拟水题)
You are given a string ss consisting of nn lowercase Latin letters. Polycarp wants to remove exactly ...
- 采用c#实现功能1
看了好多c#的菜鸟教程不如自己开始动手打代码,最终实现了功能一,参考了网上的wordcount代码发现无论是c++还是c#大部分采用的是哈希表的方法实现的,本来还想仅用循环实现遍历句子中的所有字符,即 ...
- unordered_map(hash_map)和map的比较
测试代码: #include <iostream> using namespace std; #include <string> #include <windows.h& ...