加载中...
380-O(1) 时间插入、删除和获取随机元素(Insert Delete GetRandom O(1))
发表于:2021-12-03 | 分类: 中等
字数统计: 1.9k | 阅读时长: 9分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/insert-delete-getrandom-o1

英文原文

Implement the RandomizedSet class:

  • RandomizedSet() Initializes the RandomizedSet object.
  • bool insert(int val) Inserts an item val into the set if not present. Returns true if the item was not present, false otherwise.
  • bool remove(int val) Removes an item val from the set if present. Returns true 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 to insert, remove, and getRandom.
  • 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
  • 最多调用 insertremovegetRandom 函数 2 * 105
  • 在调用 getRandom 方法时,数据结构中 至少存在一个 元素。

通过代码

官方题解

概述

我们需要在平均复杂度为 $\mathcal{O}(1)$ 实现以下操作:

  1. insert
  2. remove
  3. getRadom

让我们想想如何实现它。从 insert 开始,我们具有两个平均插入时间为 $\mathcal{O}(1)$ 的选择:

  • 哈希表:Java 中为 HashMap,Python 中为 dictionary
  • 动态数组:Java 中为 ArrayList,Python 中为 list

让我们一个个进行思考,虽然哈希表提供常数时间的插入和删除,但是实现 getRandom 时会出现问题。

getRandom 的思想是选择一个随机索引,然后使用该索引返回一个元素。而哈希表中没有索引,因此要获得真正的随机值,则要将哈希表中的键转换为列表,这需要线性时间。解决的方法是用一个列表存储值,并在该列表中实现常数时间的 getRandom

列表有索引可以实现常数时间的 insertgetRandom,则接下来的问题是如何实现常数时间的 remove

删除任意索引元素需要线性时间,这里的解决方案是总是删除最后一个元素。

  • 将要删除元素和最后一个元素交换。
  • 将最后一个元素删除。

为此,必须在常数时间获取到要删除元素的索引,因此需要一个哈希表来存储值到索引的映射。

综上所述,我们使用以下数据结构:

  • 动态数组存储元素值
  • 哈希表存储存储值到索引的映射。

方法:哈希表 + 动态数组

Insert:

  • 添加元素到动态数组。
  • 在哈希表中添加值到索引的映射

在这里插入图片描述

[insert-Python]
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
[insert-Java]
/** 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:

  • 在哈希表中查找要删除元素的索引。
  • 将要删除元素与最后一个元素交换。
  • 删除最后一个元素。
  • 更新哈希表中的对应关系。

在这里插入图片描述

[remove-Python]
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
[remove-Java]
/** 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 实现。

[getRandom-Python]
def getRandom(self) -> int: """ Get a random element from the set. """ return choice(self.list)
[getRandom-Java]
/** Get a random element from the set. */ public int getRandom() { return list.get(rand.nextInt(list.size())); }

完整代码:

[solution1-Python]
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)
[solution1-Java]
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)$,insertremove 平均时间复杂度为 $\mathcal{O}(1)$,在最坏情况下为 $\mathcal{O}(N)$ 当元素数量超过当前分配的动态数组和哈希表的容量导致空间重新分配时。
  • 空间复杂度:$O(N)$,在动态数组和哈希表分别存储了 $N$ 个元素的信息。

统计信息

通过次数 提交次数 AC比率
37971 75170 50.5%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言

相似题目

题目 难度
O(1) 时间插入、删除和获取随机元素 - 允许重复 困难
上一篇:
378-有序矩阵中第 K 小的元素(Kth Smallest Element in a Sorted Matrix)
下一篇:
381-O(1) 时间插入、删除和获取随机元素 - 允许重复(Insert Delete GetRandom O(1) - Duplicates allowed)
本文目录
本文目录