这题 很基础 很好!就是考察 各种数据结构 常见几种操作的复杂度 区分。101的 课上那张 表格应该浮现在眼前,hhh
这里说的 insert, delete 其实可以大致理解为 write, read
write & read 存在天生的 trade-off,
最快的 write 就是 append, 但是会导致 read, search 时 需要遍历,而search 又是 insert, delete, update 这些操作的 前奏。
为了让read 更快,则需要在 write 时,准备好数据的排列 (tree structure),或者 创建 metadata 来得知 elem value 和 elem position 之间的关系,这就是 index 存在的意义。这就会导致 write 在时间和空间上需要更多 复杂度。
这题提出的要求是 write & read 的复杂度是 O(1) 这个 本身就是意味着 不可能是 只有一个 data structure. 为了达到 write O(1) 就 使用 array, 同时为了 delete O(1), 就用 dict 来记录 elem_value -> elem_position 的 metadata.
还有一个要求就是 random select, 那就是说 elem 的存储 是 扁平的,抽签概率相同。
需要 练习,debug 才能 catch 到其中的 trick 和 易错地方,比如
!!在实现 remove 时,要对两个 data structure 进行修改
!!要 考虑到 elem_to_delete 和 elem_to_swap 其实是同一个 elem: elem_to_delete is the last one in the array. 这就要求 pop() 在 set_value 之后,pop() 要在最后一步。
import random
class RandomizedSet:
def __init__(self):
self.dataset = []
self.index = {}
def insert(self, val: int) -> bool:
if val not in self.index.keys():
self.dataset.append(val)
self.index[val] = len(self.dataset)-1
return True
else:
return False
def remove(self, val: int) -> bool:
if val in self.index.keys():
# 这个其实也可去掉了,因为 dataset, index 的最后操作是 pop()
# if len(self.dataset) == 1:
# self.dataset=[]
# self.index = {}
# else:
del_idx = self.index[val]
last_val = self.dataset[-1]
# ops on dataset
self.dataset[-1] = val
self.dataset[del_idx] = last_val
self.dataset.pop()
# Bug: I forget to do anything in the index dict:
# (1) delete the {val, index}
# (2) set the swap -> idx in the map. Previously, it is swap -> len(self.data)-1
# 以下两句不能互换!互换后,会出现 index out of bound error for: self.dataset[del_idx] = last_val
# 原因如下:
# edge case: the val to delete is the last val in the array
# pop() 在最后一步,是保证了 swap 其实没有发生时(del_val 本身就是最后一个elem),也能保证被removed
self.index[last_val] = del_idx
self.index.pop(val)
return True
else:
return False
def getRandom(self) -> int:
randomIdx = random.randint(0, len(self.dataset)-1)
return self.dataset[randomIdx]
def main():
randSet = RandomizedSet()
print(randSet.insert(1))
print(randSet.insert(2))
print(randSet.remove(2))
print(randSet.insert(2))
# expected
# True
# True
# True
# True
# if change the order of index ops, the actual result will be the following, which is not correct
# True
# True
# True
# False
if __name__ == "__main__":
main()