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. maven项目中没有resource文件夹的问题

    之前使用eclipse创建maven项目,文件夹都是建好的,这几次创建,都没有resource文件夹,需要手动创建resource. 现象描述 在eclipse中,创建maven项目有两种方式: 一种 ...

  2. WCF服务库创建-20140919

    1. 创建wcf服务库 2. 宿主到web程序上 // 宿主wcf服务库 RouteTable.Routes.Add(new ServiceRoute("ctserver.dll" ...

  3. Map Reduce Application(Join)

    We are going to explain how join works in MR , we will focus on reduce side join and map side join. ...

  4. simhash和minhash实现理解

    文本相似度算法 minhash minhash 1. 把文档A分词形成分词向量L 2. 使用K个hash函数,然后每个hash将L里面的分词分别进行hash,然后得到K个被hash过的集合 3. 分别 ...

  5. openstack架构

    终于正式进入 OpenStack 部分了. 今天开始,CloudMan 将带着大家一步一步揭开 OpenStack 的神秘面纱. OpenStack 已经走过了 6 个年头. 每半年会发布一个版本,版 ...

  6. Twaver的mono-desiner导出的json文件解析

    以画的交换机为例,其他大概都差不多. 利用Twaver做出交换机模型如图1所示,其中,每一个端口都是一个单独的对象.具体Twaver操作流程参见网址:http://twaver.servasoft.c ...

  7. POJ 1995 (快速幂)

    这道题普通做法会发生溢出且会超时,应当用快速幂来求解. 快速幂讲解 #include <cstdio> #include <cmath> using namespace std ...

  8. PAT 甲级 1038 Recover the Smallest Number

    https://pintia.cn/problem-sets/994805342720868352/problems/994805449625288704 Given a collection of ...

  9. 原生js实现自定义alert风格和实现

    2018年6月29 最新更新 添加函数节流,解决多次点击问题,添加单例模式,提高代码性能. <!DOCTYPE html> <html lang="en"> ...

  10. 【Docker 命令】- rmi命令

    docker rmi : 删除本地一个或多个镜像. 语法 docker rmi [OPTIONS] IMAGE [IMAGE...] OPTIONS说明: -f :强制删除: --no-prune : ...