1.题目描述

设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构。

    1. insert(val):当元素 val 不存在时,向集合中插入该项。
    2. remove(val):元素 val 存在时,从集合中移除该项。
    3. 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. 常数时间插入、删除和获取随机元素的更多相关文章

  1. Java实现 LeetCode 380 常数时间插入、删除和获取随机元素

    380. 常数时间插入.删除和获取随机元素 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构. insert(val):当元素 val 不存在时,向集合中插入该项. remove( ...

  2. 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 ...

  3. LeetCode380 常数时间插入、删除和获取随机元素

    LeetCode380 常数时间插入.删除和获取随机元素 题目要求 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构. insert(val):当元素 val 不存在时,向集合中插 ...

  4. Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素 - 允许重复

    381. O(1) 时间插入.删除和获取随机元素 - 允许重复 设计一个支持在平均 时间复杂度 O(1) 下, 执行以下操作的数据结构. 注意: 允许出现重复元素. insert(val):向集合中插 ...

  5. 381. O(1) 时间插入、删除和获取随机元素 - 允许重复

    381. O(1) 时间插入.删除和获取随机元素 - 允许重复 LeetCode_381 题目详情 题解分析 代码实现 package com.walegarrett.interview; impor ...

  6. Leetcode 381. O(1) 时间插入、删除和获取随机元素 - 允许重复

    1.题目描述 设计一个支持在平均 时间复杂度 O(1) 下, 执行以下操作的数据结构. 注意: 允许出现重复元素. insert(val):向集合中插入元素 val. remove(val):当 va ...

  7. LeetCode 380. Insert Delete GetRandom O(1) 常数时间插入、删除和获取随机元素(C++/Java)

    题目: Design a data structure that supports all following operations in averageO(1) time. insert(val): ...

  8. LeetCode 哈希表 380. 常数时间插入、删除和获取随机元素(设计数据结构 List HashMap底层 时间复杂度)

    比起之前那些问计数哈希表的题目,这道题好像更接近哈希表的底层机制. java中hashmap的实现是通过List<Node>,即链表的list,如果链表过长则换为红黑树,如果容量不足(装填 ...

  9. 381 Insert Delete GetRandom O(1) - Duplicates allowed O(1) 时间插入、删除和获取随机元素 - 允许重复

    设计一个支持在平均 时间复杂度 O(1) 下, 执行以下操作的数据结构.注意: 允许出现重复元素.    insert(val):向集合中插入元素 val.    remove(val):当 val ...

随机推荐

  1. 拓扑排序 (Ordering Tasks UVA - 10305)

    题目描述: 原题:https://vjudge.net/problem/UVA-10305 题目思路: 1.依旧是DFS 2.用邻接矩阵实现图 3.需要判断是否有环 AC代码 #include < ...

  2. java扫描控制台输入

    由于因最近练习算法的需要,加上API文档中翻译的太过模糊,做了一些小测试,算是武断的记下一些个人结论. Scanner cin = new Scanner(System.in); 对于cin.next ...

  3. Ubuntu—终端命令调整窗口的大小

    1,查看窗口大小 current 1280x768 是我当前电脑的窗口大小,下面提供的是可以修改的窗口大小. $ xrandr 2.修改窗口大小 示例: $ xrandr -s 1024x768

  4. 小米 OJ 编程比赛 02 月常规赛

    Carryon 数数字 描述 Carryon 最近迷上了数数字,然后 Starry 给了他一个区间[l,r] ,然后提了几个要求, 需要将 ll 到 rr 之间的数全部转化成 16 进制,然后连起来. ...

  5. "Generative Adversarial Nets" Notes

    - Ian J.Goodfellow 中文翻译:https://blog.csdn.net/wspba/article/details/54577236 代码实现:https://github.com ...

  6. Block的声明与定义语法

    Block的声明 Block的声明与函数指针的声明类似 返回值类型(^变量名)(参数列表) Block的定义 ^返回值类型(参数列表) { 表达式 } 其中: 1 如果返回值类型是void,可以省略 ...

  7. C语言特殊符号

    -> ->在C语言中称为间接引用运算符,是二目运算符,优先级同成员运算符“.”.用法:p->a,其中p是指向一个结构体的指针,a是这个结构体类型的一个成员.表达式p->a引用了 ...

  8. (转)Linux常用性能检测命令

    一.uptime     Uptime命令的显示结果包括服务器已经运行了多长时间,有多少登陆用户和对服务器性能的总体评估(load average).load average值分别记录了上个1分钟,5 ...

  9. new关键字 、this关键字、base关键字

    使用new,所做的三件事: 1. (类是引用对象,引用对象是在堆中开辟空间)在堆中开辟空间 2. 在开辟的堆空间中创建对象 3. 调用对象的构建函数 4. 隐藏父类成员:子类的成员可以与隐藏从父类继承 ...

  10. title & abbr & tooltips

    title & abbr & tooltips https://dmitrybaranovskiy.github.io/raphael/ <abbr title="Sc ...