Design a data structure that supports all following operations in average O(1) time.

Note: Duplicate elements are allowed.

  1. insert(val): Inserts an item val to the collection.
  2. remove(val): Removes an item val from the collection if present.
  3. getRandom: Returns a random element from current collection of elements. The probability of each element being returned is linearly related to the number of same value the collection contains.

Example:

// Init an empty collection.
RandomizedCollection collection = new RandomizedCollection(); // Inserts 1 to the collection. Returns true as the collection did not contain 1.
collection.insert(1); // Inserts another 1 to the collection. Returns false as the collection contained 1. Collection now contains [1,1].
collection.insert(1); // Inserts 2 to the collection, returns true. Collection now contains [1,1,2].
collection.insert(2); // getRandom should return 1 with the probability 2/3, and returns 2 with the probability 1/3.
collection.getRandom(); // Removes 1 from the collection, returns true. Collection now contains [1,2].
collection.remove(1); // getRandom should return 1 and 2 both equally likely.
collection.getRandom();

这题是之前那道 Insert Delete GetRandom O(1) 的拓展,与其不同的是,之前那道题不能有重复数字,而这道题可以有,那么就不能像之前那道题那样建立每个数字和其坐标的映射了,但是我们可以建立数字和其所有出现位置的集合之间的映射,虽然写法略有不同,但是思路和之前那题完全一样,都是将数组最后一个位置的元素和要删除的元素交换位置,然后删掉最后一个位置上的元素。对于 insert 函数,我们将要插入的数字在 nums 中的位置加入 m[val] 数组的末尾,然后在数组 nums 末尾加入 val,我们判断是否有重复只要看 m[val] 数组只有刚加的 val 一个值还是有多个值。remove 函数是这题的难点,我们首先看 HashMap 中有没有 val,没有的话直接返回 false。然后我们取出 nums 的尾元素,把尾元素 HashMap 中的位置数组中的最后一个位置更新为 m[val] 的尾元素,这样我们就可以删掉 m[val] 的尾元素了,如果 m[val] 只有一个元素,那么我们把这个映射直接删除。然后将 nums 数组中的尾元素删除,并把尾元素赋给 val 所在的位置,注意我们在建立 HashMap 的映射的时候需要用堆而不是普通的 vector 数组,因为我们每次 remove 操作后都会移除 nums 数组的尾元素,如果我们用 vector 来保存数字的坐标,而且只移出末尾数字的话,有可能出现前面的坐标大小超过了此时 nums 的大小的情况,就会出错,所以我们用优先队列对所有的相同数字的坐标进行自动排序,每次把最大位置的坐标移出即可,参见代码如下:

解法一:

class RandomizedCollection {
public:
RandomizedCollection() {}
bool insert(int val) {
m[val].push(nums.size());
nums.push_back(val);
return m[val].size() == ;
}
bool remove(int val) {
if (m[val].empty()) return false;
int idx = m[val].top();
m[val].pop();
if (nums.size() - != idx) {
int t = nums.back();
nums[idx] = t;
m[t].pop();
m[t].push(idx);
}
nums.pop_back();
return true;
}
int getRandom() {
return nums[rand() % nums.size()];
}
private:
vector<int> nums;
unordered_map<int, priority_queue<int>> m;
};

有网友指出上面的方法其实不是真正的 O(1) 时间复杂度,因为优先队列的 push 不是常数级的,博主一看果然是这样的,为了严格的遵守 O(1) 的时间复杂度,我们将优先队列换成 unordered_set,其插入删除的操作都是常数量级的,其他部分基本不用变,参见代码如下:

解法二:

class RandomizedCollection {
public:
RandomizedCollection() {}
bool insert(int val) {
m[val].insert(nums.size());
nums.push_back(val);
return m[val].size() == ;
}
bool remove(int val) {
if (m[val].empty()) return false;
int idx = *m[val].begin();
m[val].erase(idx);
if (nums.size() - != idx) {
int t = nums.back();
nums[idx] = t;
m[t].erase(nums.size() - );
m[t].insert(idx);
}
nums.pop_back();
return true;
}
int getRandom() {
return nums[rand() % nums.size()];
} private:
vector<int> nums;
unordered_map<int, unordered_set<int>> m;
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/381

类似题目:

Insert Delete GetRandom O(1)

参考资料:

https://leetcode.com/problems/insert-delete-getrandom-o1-duplicates-allowed/

https://leetcode.com/problems/insert-delete-getrandom-o1-duplicates-allowed/discuss/85635/c-two-solutions

https://leetcode.com/problems/insert-delete-getrandom-o1-duplicates-allowed/discuss/85541/C%2B%2B-128m-Solution-Real-O(1)-Solution

https://leetcode.com/problems/insert-delete-getrandom-o1-duplicates-allowed/discuss/197641/C%2B%2B-30-ms-hashmap-hashset-and-vector

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Insert Delete GetRandom O(1) - Duplicates allowed 常数时间内插入删除和获得随机数 - 允许重复的更多相关文章

  1. [LeetCode] 381. Insert Delete GetRandom O(1) - Duplicates allowed 常数时间内插入删除和获得随机数 - 允许重复

    Design a data structure that supports all following operations in average O(1) time. Note: Duplicate ...

  2. [leetcode]381. Insert Delete GetRandom O(1) - Duplicates allowed常数时间插入删除取随机值

    Design a data structure that supports all following operations in average O(1) time. Note: Duplicate ...

  3. Leetcode: Insert Delete GetRandom O(1) - Duplicates allowed

    Design a data structure that supports all following operations in average O(1) time. Note: Duplicate ...

  4. leetcode 380. Insert Delete GetRandom O(1) 、381. Insert Delete GetRandom O(1) - Duplicates allowed

    380. Insert Delete GetRandom O(1) 实现插入.删除.获得随机数功能,且时间复杂度都在O(1).实际上在插入.删除两个功能中都包含了查找功能,当然查找也必须是O(1). ...

  5. 381. Insert Delete GetRandom O(1) - Duplicates allowed

    Design a data structure that supports all following operations in average O(1) time. Note: Duplicate ...

  6. [LeetCode] Insert Delete GetRandom O(1) 常数时间内插入删除和获得随机数

    Design a data structure that supports all following operations in average O(1) time. insert(val): In ...

  7. [LeetCode] 380. Insert Delete GetRandom O(1) 常数时间内插入删除和获得随机数

    Design a data structure that supports all following operations in average O(1) time. insert(val): In ...

  8. [LeetCode] 381. Insert Delete GetRandom O(1) - Duplicates allowed 插入删除和获得随机数O(1)时间 - 允许重复

    Design a data structure that supports all following operations in average O(1) time. Note: Duplicate ...

  9. LeetCode 381. Insert Delete GetRandom O(1) - Duplicates allowed

    原题链接在这里:https://leetcode.com/problems/insert-delete-getrandom-o1-duplicates-allowed/?tab=Description ...

随机推荐

  1. PHP用单例模式实现一个数据库类

    使用单例模式的出发点: 1.php的应用主要在于数据库应用, 所以一个应用中会存在大量的数据库操作, 使用单例模式, 则可以避免大量的new 操作消耗的资源. 2.如果系统中需要有一个类来全局控制某些 ...

  2. TCP四步挥手的各种状态转换图

    对于TCP四步挥手时的各种状态转换,网上有很多资料.但是有很多描述不是很容易理解,甚至是描述错误,不如这篇文章里http://www.cnblogs.com/Jessy/p/3535612.html# ...

  3. ASP.NET Core 中文文档 第三章 原理(8)日志

    原文:Logging 作者:Steve Smith 翻译:刘怡(AlexLEWIS) 校对:何镇汐.许登洋(Seay) ASP.NET Core 内建支持日志,也允许开发人员轻松切换为他们想用的其他日 ...

  4. intellij idea 15 修改基础配置加载路径

    一.概述 intellij idea 15 默认配置的启动加载路径是"C:\Users\Administrator.IntelliJIdea15",这样会导致占用C盘的空间越来越多 ...

  5. java范型集合中的成员排序

    范型集合中的类是JsonObject,不是自定义类,如果是自定义类就直接取要比较的字段值. ArrayList<JSONObject> TList = new ArrayList<J ...

  6. Atitit.数据采集器 dataspider

    Atitit.数据采集器 dataspider /atiplat_cms/src/com/attilax/WebInfoX.java  @dep http://cl.cmcher.com/thread ...

  7. php的http_build_query使用

    http_build_query生成 url-encoded 之后的请求字符串 1.使用键值对,关联数组: <?php $data = array('foo'=>'bar', 'baz'= ...

  8. React Native初探

    前言 很久之前就想研究React Native了,但是一直没有落地的机会,我一直认为一个技术要有落地的场景才有研究的意义,刚好最近迎来了新的APP,在可控的范围内,我们可以在上面做任何想做的事情. P ...

  9. Google C++单元测试框架GoogleTest---值参数化测试

    值参数化测试允许您使用不同的参数测试代码,而无需编写同一测试的多个副本. 假设您为代码编写测试,然后意识到您的代码受到布尔参数的影响. TEST(MyCodeTest, TestFoo) { // A ...

  10. xamarin 一般错误解决办法

    1. android_m2repository_r错误 问题描述: Unzipping failed. Please download https://dl-ssl.google.com/androi ...