力扣380. O(1) 时间插入、删除和获取随机元素

123 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情

力扣380. O(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) 链接:leetcode-cn.com/problems/in… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、思路分析:

  1. 这道题考察了什么思想?你的思路是什么?

    O(1)的时间复杂度,这让我想起了哈希表,初始化一个list,名为nums,另外为了记录各数的索引,避免删除时遍历nums,我们建立一个索引字典index_map。插入时,判断值是否是index_map的键,如果不是,就append到nums中,另外添加索引,返回True。如果是,就返回False。

    移除时,判断该值是否是字典的键,如果不是就返回False,如果是,因为要保证时间复杂度为1,所以我们将最后一个元素val覆盖到要删除的值的位置,并更新最后一个元素val的索引。

    随机获取值时,我们利用choice函数, choice() 方法返回一个列表,元组或字符串的随机项。

  2. 做题的时候是不是一次通过的,遇到了什么问题,需要注意什么细节?

    不是,在insert的时候,我先append元素val,然后再添加索引,但是我的索引是nums的长度,所以有两种解决办法:

             self.nums.append(val)
             self.index_map[val] = len(self.nums)-1
    
             self.index_map[val] = len(self.nums
             self.nums.append(val)
    

    这样就能通过啦~

  3. 有几种解法,哪种解法时间复杂度最低,哪种解法空间复杂度最低,最优解法是什么?其他人的题解是什么,谁的效率更好一些?用不同语言实现的话,哪个语言速度最快?

    我们能够看到我这样的解法执行用时和内存消耗均不太好,所以看看大佬们是怎样解决这个问题的吧~

image.png

```
 class RandomizedSet:
 ​
     def __init__(self):
         self.nums = []
         self.pos = {}
         
     def insert(self, val):
         if val not in self.pos:
             self.nums.append(val)
             self.pos[val] = len(self.nums) - 1
             return True
         return False
 ​
     def remove(self, val):
         if val in self.pos:
             idx, last = self.pos[val], self.nums[-1]
             self.nums[idx], self.pos[last] = last, idx
             self.nums.pop(-1)
             del self.pos[val]
             return True
         return False
             
     def getRandom(self):
         return random.choice(self.nums)
 ​
 # 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()
 ​
 作者:Jam007
 链接:https://leetcode-cn.com/problems/insert-delete-getrandom-o1/solution/by-jam007-r8xf/
 来源:力扣(LeetCode)
 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
```

三、AC 代码:

 class RandomizedSet:
 ​
     def __init__(self):
         self.nums = []
         self.index_map = {}
 ​
     def insert(self, val: int) -> bool:
         if val in self.index_map:
             return False
         self.index_map[val] = len(self.nums)
         self.nums.append(val)
         return True
 ​
 ​
     def remove(self, val: int) -> bool:
         if val not in self.index_map:
             return False
         index = self.index_map[val]
         self.nums[index] = self.nums[-1]
         self.index_map[self.nums[-1]] = index
         self.nums.pop()
         del self.index_map[val]
         return True
 ​
     def getRandom(self) -> int:
         return choice(self.nums)
 ​
 ​
 # 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()

image.png

image.png

四、总结:

做这类题目,每一步都需要注意,步骤的先后均会导致不同的结果。