目录
1. 题目描述
实现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 。
提示:
-2^31 <= val <= 2^31 - 1
- 最多调用
insert
、remove
和getRandom
函数2 *
10^5
次 - 在调用
getRandom
方法时,数据结构中 至少存在一个 元素。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/insert-delete-getrandom-o1
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
2. 解题分析
重点是采用什么样的数据结构来存储数据,以及每次操作后的自动动态调整,以确保每次操作的平均处理时间复杂度为O(1).
首先,要在O(1)时间内的插入删除,肯定要利用哈希表的。在python中用set()可以实现O(1)的insert和remove操作,但是如何实现O(1)的随机采样呢?
Python中random.choice()可以进行随机采样,但是这个对set()不起作用,因为random.choice()要求操作对象是subscriptable,即可以通过索引进行随机访问,换句话说要求像数组一样的有序数据结构,或者说支持随机访问的迭代器。比如说,python中list或者tuple等。
所以,需要考虑结合哈希表和支持随机访问的迭代器,让两者相互补充的数据存储访问管理方式。
考虑用一个数组存储数据集合,数组相当于是从索引到数据的映射;然后用哈希表存储从数据到索引的映射。这样的话:
- insert(): 插入一个数据时,首先从哈希表中查询这个数据是否已经存在。如果存在的话返回False;如果不存在的话,往数组尾部加入该元素,相应地往哈希表加入该{data:index}映射项
- getRandom(): 随机获取一个元素,就用random.choice()从数组中随机抽取一个即可。此操作由于并不需要删除(即不是pop操作),不涉及哈希表
- remove(): 直接对数组进行随机remove一个元素的操作非常耗时,肯定不行。但是由于哈希表维护了数据的索引信息,所以可以将待删除的元素(如果存在的话)与数组最末尾的元素先交换位置,然后再从数组末尾删除该元素,即可实现O(1)复杂度的操作
在以下代码实现中,另外用一个数来记录当前数组中的元素的个数,以方便各种操作的实现(非必须,但是能改善一些时间性能)。
3. 代码实现
class RandomizedSet:
def __init__(self):
self.arr = []
self.h = dict()
self.num = 0
def insert(self, val: int) -> bool:
if val in self.h:
return False
else:
self.arr.append(val)
self.h[val] = self.num
self.num += 1
return True
def remove(self, val: int) -> bool:
if val not in self.h:
return False
else:
# exchange the location of val and the last element
val_idx = self.h[val]
last_val= self.arr[self.num-1]
self.arr[val_idx], self.arr[self.num-1]= last_val, val
self.h[last_val] = val_idx
# self.h[val] = self.num - 1 #Not necessarily needed
# pop the specified element, and then, updat num, h
self.arr.pop()
self.num -= 1
self.h.pop(val)
return True
def getRandom(self) -> int:
return random.choice(self.arr)
# Your RandomizedSet object will be instantiated and called as such:
# obj = RandomizedSet()
# param_1 = obj.insert(val)
# param_2 = obj.remove(val)
# param_3 = obj.getRandom()
执行用时:396 ms, 在所有 Python3 提交中击败了79.00%的用户
内存消耗:49.8 MB, 在所有 Python3 提交中击败了45.39%的用户
回到主目录:笨牛慢耕的Leetcode解题笔记(动态更新。。。)