原文链接: https://leetcode-cn.com/problems/insert-delete-getrandom-o1
英文原文
Implement the RandomizedSet
class:
RandomizedSet()
Initializes theRandomizedSet
object.bool insert(int val)
Inserts an itemval
into the set if not present. Returnstrue
if the item was not present,false
otherwise.bool remove(int val)
Removes an itemval
from the set if present. Returnstrue
if the item was present,false
otherwise.int getRandom()
Returns a random element from the current set of elements (it's guaranteed that at least one element exists when this method is called). Each element must have the same probability of being returned.
You must implement the functions of the class such that each function works in average O(1)
time complexity.
Example 1:
Input ["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"] [[], [1], [2], [2], [], [1], [2], []] Output [null, true, false, true, 2, true, false, 2] Explanation RandomizedSet randomizedSet = new RandomizedSet(); randomizedSet.insert(1); // Inserts 1 to the set. Returns true as 1 was inserted successfully. randomizedSet.remove(2); // Returns false as 2 does not exist in the set. randomizedSet.insert(2); // Inserts 2 to the set, returns true. Set now contains [1,2]. randomizedSet.getRandom(); // getRandom() should return either 1 or 2 randomly. randomizedSet.remove(1); // Removes 1 from the set, returns true. Set now contains [2]. randomizedSet.insert(2); // 2 was already in the set, so return false. randomizedSet.getRandom(); // Since 2 is the only number in the set, getRandom() will always return 2.
Constraints:
-231 <= val <= 231 - 1
- At most
2 *
105
calls will be made toinsert
,remove
, andgetRandom
. - There will be at least one element in the data structure when
getRandom
is called.
中文题目
实现RandomizedSet
类:
RandomizedSet()
初始化RandomizedSet
对象bool insert(int val)
当元素val
不存在时,向集合中插入该项,并返回true
;否则,返回false
。bool remove(int val)
当元素val
存在时,从集合中移除该项,并返回true
;否则,返回false
。int getRandom()
随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。每个元素应该有 相同的概率 被返回。
你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1)
。
示例:
输入 ["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"] [[], [1], [2], [2], [], [1], [2], []] 输出 [null, true, false, true, 2, true, false, 2] 解释 RandomizedSet randomizedSet = new RandomizedSet(); randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。 randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。 randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。 randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。 randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。 randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。 randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
提示:
-231 <= val <= 231 - 1
- 最多调用
insert
、remove
和getRandom
函数2 *
105
次 - 在调用
getRandom
方法时,数据结构中 至少存在一个 元素。
通过代码
官方题解
概述
我们需要在平均复杂度为 $\mathcal{O}(1)$ 实现以下操作:
insert
remove
getRadom
让我们想想如何实现它。从 insert
开始,我们具有两个平均插入时间为 $\mathcal{O}(1)$ 的选择:
- 哈希表:Java 中为
HashMap
,Python 中为dictionary
。 - 动态数组:Java 中为
ArrayList
,Python 中为list
。
让我们一个个进行思考,虽然哈希表提供常数时间的插入和删除,但是实现 getRandom
时会出现问题。
getRandom
的思想是选择一个随机索引,然后使用该索引返回一个元素。而哈希表中没有索引,因此要获得真正的随机值,则要将哈希表中的键转换为列表,这需要线性时间。解决的方法是用一个列表存储值,并在该列表中实现常数时间的 getRandom
。
列表有索引可以实现常数时间的 insert
和 getRandom
,则接下来的问题是如何实现常数时间的 remove
。
删除任意索引元素需要线性时间,这里的解决方案是总是删除最后一个元素。
- 将要删除元素和最后一个元素交换。
- 将最后一个元素删除。
为此,必须在常数时间获取到要删除元素的索引,因此需要一个哈希表来存储值到索引的映射。
综上所述,我们使用以下数据结构:
- 动态数组存储元素值
- 哈希表存储存储值到索引的映射。
方法:哈希表 + 动态数组
Insert:
- 添加元素到动态数组。
- 在哈希表中添加值到索引的映射
def insert(self, val: int) -> bool:
"""
Inserts a value to the set. Returns true if the set did not already contain the specified element.
"""
if val in self.dict:
return False
self.dict[val] = len(self.list)
self.list.append(val)
return True
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
public boolean insert(int val) {
if (dict.containsKey(val)) return false;
dict.put(val, list.size());
list.add(list.size(), val);
return true;
}
remove:
- 在哈希表中查找要删除元素的索引。
- 将要删除元素与最后一个元素交换。
- 删除最后一个元素。
- 更新哈希表中的对应关系。
def remove(self, val: int) -> bool:
"""
Removes a value from the set. Returns true if the set contained the specified element.
"""
if val in self.dict:
# move the last element to the place idx of the element to delete
last_element, idx = self.list[-1], self.dict[val]
self.list[idx], self.dict[last_element] = last_element, idx
# delete the last element
self.list.pop()
del self.dict[val]
return True
return False
/** Removes a value from the set. Returns true if the set contained the specified element. */
public boolean remove(int val) {
if (! dict.containsKey(val)) return false;
// move the last element to the place idx of the element to delete
int lastElement = list.get(list.size() - 1);
int idx = dict.get(val);
list.set(idx, lastElement);
dict.put(lastElement, idx);
// delete the last element
list.remove(list.size() - 1);
dict.remove(val);
return true;
}
getRandom:
借助 Python 中的 random.choice
和 Java 中 的 Random
实现。
def getRandom(self) -> int:
"""
Get a random element from the set.
"""
return choice(self.list)
/** Get a random element from the set. */
public int getRandom() {
return list.get(rand.nextInt(list.size()));
}
完整代码:
from random import choice
class RandomizedSet():
def __init__(self):
"""
Initialize your data structure here.
"""
self.dict = {}
self.list = []
def insert(self, val: int) -> bool:
"""
Inserts a value to the set. Returns true if the set did not already contain the specified element.
"""
if val in self.dict:
return False
self.dict[val] = len(self.list)
self.list.append(val)
return True
def remove(self, val: int) -> bool:
"""
Removes a value from the set. Returns true if the set contained the specified element.
"""
if val in self.dict:
# move the last element to the place idx of the element to delete
last_element, idx = self.list[-1], self.dict[val]
self.list[idx], self.dict[last_element] = last_element, idx
# delete the last element
self.list.pop()
del self.dict[val]
return True
return False
def getRandom(self) -> int:
"""
Get a random element from the set.
"""
return choice(self.list)
class RandomizedSet {
Map<Integer, Integer> dict;
List<Integer> list;
Random rand = new Random();
/** Initialize your data structure here. */
public RandomizedSet() {
dict = new HashMap();
list = new ArrayList();
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
public boolean insert(int val) {
if (dict.containsKey(val)) return false;
dict.put(val, list.size());
list.add(list.size(), val);
return true;
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
public boolean remove(int val) {
if (! dict.containsKey(val)) return false;
// move the last element to the place idx of the element to delete
int lastElement = list.get(list.size() - 1);
int idx = dict.get(val);
list.set(idx, lastElement);
dict.put(lastElement, idx);
// delete the last element
list.remove(list.size() - 1);
dict.remove(val);
return true;
}
/** Get a random element from the set. */
public int getRandom() {
return list.get(rand.nextInt(list.size()));
}
}
复杂度分析
- 时间复杂度:
getRandom
时间复杂度为 $\mathcal{O}(1)$,insert
和remove
平均时间复杂度为 $\mathcal{O}(1)$,在最坏情况下为 $\mathcal{O}(N)$ 当元素数量超过当前分配的动态数组和哈希表的容量导致空间重新分配时。 - 空间复杂度:$O(N)$,在动态数组和哈希表分别存储了 $N$ 个元素的信息。
统计信息
通过次数 | 提交次数 | AC比率 |
---|---|---|
37971 | 75170 | 50.5% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|
相似题目
题目 | 难度 |
---|---|
O(1) 时间插入、删除和获取随机元素 - 允许重复 | 困难 |